mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 03:36:44 +02:00
Dev/zhaopengwang/test/37733 UI test fancyzones editor (#37769)
* first launch test * add FancyZonesEditorHelper * click monitor test and add FindByAccessibilityId function * add ui initialization tests and add exit scope exe function * add cleanup test function and change file init * add TemplateLayoutsTest and add LayoutTypeEnumExtension.cs and Element class add sendkey function * add UI Initialize Test * add OpenEditLayoutDialog test case and add By type * add LayoutHotkeysTest * add EditLayoutTests and add element drag function * add DeleteLayouTest and change cleanup to base class and change FindByAccessibilityId to By.AccessibilityId * add DefaultLayoutsTest * add CustomLayoutsTest * add CreateLayoutTest * add CopyLayoutTest * add ApplyLayoutTest * add some cleanup code * fix spelling error * fix DeleteLayoutWithHotkey test code bug * change code * fix restart exe some bug * move first lunch text code to new file * test write file error * fix test code init fancyzone file error * test maxsize button * get current window size * change layout count * change test case work windows size * change fancyzone editor window size * change fancyzone editor window size and change element move rule * change window size --------- Co-authored-by: Zhaopeng Wang <zhaopengwang@microsoft.com>
This commit is contained in:
@@ -11,17 +11,40 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// </summary>
|
||||
public class By
|
||||
{
|
||||
private readonly OpenQA.Selenium.By by;
|
||||
private readonly OpenQA.Selenium.By? by;
|
||||
private readonly bool isAccessibilityId;
|
||||
private readonly string? accessibilityId;
|
||||
|
||||
private By(OpenQA.Selenium.By by)
|
||||
{
|
||||
isAccessibilityId = false;
|
||||
this.by = by;
|
||||
}
|
||||
|
||||
private By(string accessibilityId)
|
||||
{
|
||||
isAccessibilityId = true;
|
||||
this.accessibilityId = accessibilityId;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// override ToString to return detailed debugging content provided by OpenQA.Selenium.By
|
||||
return this.by.ToString();
|
||||
return this.GetAccessibilityId();
|
||||
}
|
||||
|
||||
public bool GetIsAccessibilityId() => this.isAccessibilityId;
|
||||
|
||||
public string GetAccessibilityId()
|
||||
{
|
||||
if (this.isAccessibilityId)
|
||||
{
|
||||
return this.accessibilityId!;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.by!.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +54,13 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <returns>A By object.</returns>
|
||||
public static By Name(string name) => new By(OpenQA.Selenium.By.Name(name));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a By object using the className attribute.
|
||||
/// </summary>
|
||||
/// <param name="className">The className attribute to search for.</param>
|
||||
/// <returns>A By object.</returns>
|
||||
public static By ClassName(string className) => new By(OpenQA.Selenium.By.ClassName(className));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a By object using the ID attribute.
|
||||
/// </summary>
|
||||
@@ -38,6 +68,13 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <returns>A By object.</returns>
|
||||
public static By Id(string id) => new By(OpenQA.Selenium.By.Id(id));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a By object using the ID attribute.
|
||||
/// </summary>
|
||||
/// <param name="accessibilityId">The ID attribute to search for.</param>
|
||||
/// <returns>A By object.</returns>
|
||||
public static By AccessibilityId(string accessibilityId) => new By(accessibilityId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a By object using the XPath expression.
|
||||
/// </summary>
|
||||
@@ -70,6 +107,6 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// Converts the By object to an OpenQA.Selenium.By object.
|
||||
/// </summary>
|
||||
/// <returns>An OpenQA.Selenium.By object.</returns>
|
||||
internal OpenQA.Selenium.By ToSeleniumBy() => by;
|
||||
internal OpenQA.Selenium.By ToSeleniumBy() => by!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
using ABI.Windows.Foundation;
|
||||
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
@@ -63,6 +68,14 @@ namespace Microsoft.PowerToys.UITest
|
||||
get { return this.windowsElement?.Selected ?? false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Rect of the UI element.
|
||||
/// </summary>
|
||||
public Rectangle? Rect
|
||||
{
|
||||
get { return this.windowsElement?.Rect; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AutomationID of the UI element.
|
||||
/// </summary>
|
||||
@@ -138,6 +151,54 @@ namespace Microsoft.PowerToys.UITest
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drag element move offset.
|
||||
/// </summary>
|
||||
/// <param name="offsetX">The offsetX to move.</param>
|
||||
/// <param name="offsetY">The offsetY to move.</param>
|
||||
public void Drag(int offsetX, int offsetY)
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
actions.MoveToElement(windowElement).MoveByOffset(10, 10).ClickAndHold(windowElement).MoveByOffset(offsetX, offsetY).Release();
|
||||
actions.Build().Perform();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drag element move to other element.
|
||||
/// </summary>
|
||||
/// <param name="element">Move to this element.</param>
|
||||
public void Drag(Element element)
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
actions.MoveToElement(windowElement).ClickAndHold();
|
||||
Assert.IsNotNull(element.windowsElement, "element is null");
|
||||
int dx = (element.windowsElement.Rect.X - windowElement.Rect.X) / 10;
|
||||
int dy = (element.windowsElement.Rect.Y - windowElement.Rect.Y) / 10;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
actions.MoveByOffset(dx, dy);
|
||||
}
|
||||
|
||||
actions.Release();
|
||||
actions.Build().Perform();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send Key of the element.
|
||||
/// </summary>
|
||||
/// <param name="key">The Key to Send.</param>
|
||||
public void SendKeys(string key)
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
windowElement.SendKeys(key);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute value of the UI element.
|
||||
/// </summary>
|
||||
@@ -222,8 +283,16 @@ namespace Microsoft.PowerToys.UITest
|
||||
var foundElements = FindHelper.FindAll<T, AppiumWebElement>(
|
||||
() =>
|
||||
{
|
||||
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
|
||||
return elements;
|
||||
if (by.GetIsAccessibilityId())
|
||||
{
|
||||
var elements = this.windowsElement.FindElementsByAccessibilityId(by.GetAccessibilityId());
|
||||
return elements;
|
||||
}
|
||||
else
|
||||
{
|
||||
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
|
||||
return elements;
|
||||
}
|
||||
},
|
||||
this.driver,
|
||||
timeoutMS);
|
||||
|
||||
@@ -17,6 +17,19 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// </summary>
|
||||
internal static class FindHelper
|
||||
{
|
||||
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<IReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
|
||||
where T : Element, new()
|
||||
{
|
||||
var items = findElementsFunc();
|
||||
var res = items.Select(item =>
|
||||
{
|
||||
var element = item as WindowsElement;
|
||||
return NewElement<T>(element, driver, timeoutMS);
|
||||
}).Where(item => item.IsMatchingTarget()).ToList();
|
||||
|
||||
return new ReadOnlyCollection<T>(res);
|
||||
}
|
||||
|
||||
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<ReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
|
||||
where T : Element, new()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Xml.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
@@ -98,8 +99,16 @@ namespace Microsoft.PowerToys.UITest
|
||||
var foundElements = FindHelper.FindAll<T, WindowsElement>(
|
||||
() =>
|
||||
{
|
||||
var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy());
|
||||
return elements;
|
||||
if (by.GetIsAccessibilityId())
|
||||
{
|
||||
var elements = this.WindowsDriver.FindElementsByAccessibilityId(by.GetAccessibilityId());
|
||||
return elements;
|
||||
}
|
||||
else
|
||||
{
|
||||
var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy());
|
||||
return elements;
|
||||
}
|
||||
},
|
||||
this.WindowsDriver,
|
||||
timeoutMS);
|
||||
@@ -145,6 +154,39 @@ namespace Microsoft.PowerToys.UITest
|
||||
return this.FindAll<Element>(By.Name(name), timeoutMS);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keyboard Action key.
|
||||
/// </summary>
|
||||
/// <param name="key1">The Keys1 to click.</param>
|
||||
/// <param name="key2">The Keys2 to click.</param>
|
||||
/// <param name="key3">The Keys3 to click.</param>
|
||||
/// <param name="key4">The Keys4 to click.</param>
|
||||
public void KeyboardAction(string key1, string key2 = "", string key3 = "", string key4 = "")
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(key2))
|
||||
{
|
||||
actions.SendKeys(key1);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(key3))
|
||||
{
|
||||
actions.SendKeys(key1).SendKeys(key2);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(key4))
|
||||
{
|
||||
actions.SendKeys(key1).SendKeys(key2).SendKeys(key3);
|
||||
}
|
||||
else
|
||||
{
|
||||
actions.SendKeys(key1).SendKeys(key2).SendKeys(key3).SendKeys(key4);
|
||||
}
|
||||
|
||||
actions.Release();
|
||||
actions.Build().Perform();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches to an existing PowerToys module.
|
||||
/// </summary>
|
||||
@@ -189,5 +231,28 @@ namespace Microsoft.PowerToys.UITest
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates a manual operation on the element.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform on the element.</param>
|
||||
/// <param name="msPreAction">The number of milliseconds to wait before the action. Default value is 500 ms</param>
|
||||
/// <param name="msPostAction">The number of milliseconds to wait after the action. Default value is 500 ms</param>
|
||||
protected void PerformAction(Action<Actions, WindowsDriver<WindowsElement>> action, int msPreAction = 500, int msPostAction = 500)
|
||||
{
|
||||
if (msPreAction > 0)
|
||||
{
|
||||
Task.Delay(msPreAction).Wait();
|
||||
}
|
||||
|
||||
var windowsDriver = this.WindowsDriver;
|
||||
Actions actions = new Actions(this.WindowsDriver);
|
||||
action(actions, windowsDriver);
|
||||
|
||||
if (msPostAction > 0)
|
||||
{
|
||||
Task.Delay(msPostAction).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
@@ -19,15 +20,19 @@ namespace Microsoft.PowerToys.UITest
|
||||
// Default session path is PowerToys settings dashboard
|
||||
private readonly string sessionPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.PowerToysSettings);
|
||||
|
||||
private string? locationPath;
|
||||
|
||||
private WindowsDriver<WindowsElement> Root { get; set; }
|
||||
|
||||
private WindowsDriver<WindowsElement>? Driver { get; set; }
|
||||
|
||||
private Process? appDriver;
|
||||
|
||||
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
|
||||
public SessionHelper(PowerToysModule scope)
|
||||
{
|
||||
this.sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
|
||||
this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
var winAppDriverProcessInfo = new ProcessStartInfo
|
||||
{
|
||||
@@ -49,17 +54,12 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// Initializes the test environment.
|
||||
/// </summary>
|
||||
/// <param name="scope">The PowerToys module to start.</param>
|
||||
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
|
||||
public SessionHelper Init()
|
||||
{
|
||||
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
this.StartExe(path + this.sessionPath);
|
||||
this.StartExe(locationPath + this.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);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -68,9 +68,11 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// </summary>
|
||||
public void Cleanup()
|
||||
{
|
||||
ExitScopeExe();
|
||||
try
|
||||
{
|
||||
appDriver?.Kill();
|
||||
appDriver?.WaitForExit(); // Optional: Wait for the process to exit
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -87,7 +89,53 @@ namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
var opts = new AppiumOptions();
|
||||
opts.AddAdditionalCapability("app", appPath);
|
||||
Console.WriteLine($"appPath: {appPath}");
|
||||
this.Driver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), opts);
|
||||
|
||||
// Set default timeout to 5 seconds
|
||||
this.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exit a exe.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the application executable.</param>
|
||||
public void ExitExe(string path)
|
||||
{
|
||||
// Exit Exe
|
||||
string exeName = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
// PowerToys.FancyZonesEditor
|
||||
Process[] processes = Process.GetProcessesByName(exeName);
|
||||
foreach (Process process in processes)
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
process.WaitForExit(); // Optional: Wait for the process to exit
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail($"Failed to terminate process {process.ProcessName} (ID: {process.Id}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exit now exe.
|
||||
/// </summary>
|
||||
public void ExitScopeExe()
|
||||
{
|
||||
ExitExe(sessionPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restarts now exe and takes control of it.
|
||||
/// </summary>
|
||||
public void RestartScopeExe()
|
||||
{
|
||||
ExitExe(sessionPath);
|
||||
StartExe(locationPath + sessionPath);
|
||||
}
|
||||
|
||||
public WindowsDriver<WindowsElement> GetRoot() => this.Root;
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
@@ -31,11 +32,6 @@ namespace Microsoft.PowerToys.UITest
|
||||
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver());
|
||||
}
|
||||
|
||||
~UITestBase()
|
||||
{
|
||||
this.sessionHelper.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the test.
|
||||
/// </summary>
|
||||
@@ -53,6 +49,15 @@ namespace Microsoft.PowerToys.UITest
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UnInitializes the test.
|
||||
/// </summary>
|
||||
[TestCleanup]
|
||||
public void TestClean()
|
||||
{
|
||||
this.sessionHelper.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an element by selector.
|
||||
/// Shortcut for this.Session.Find<T>(by, timeoutMS)
|
||||
@@ -153,5 +158,24 @@ namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
return this.Session.FindAll<Element>(By.Name(name), timeoutMS);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart scope exe.
|
||||
/// </summary>
|
||||
public void RestartScopeExe()
|
||||
{
|
||||
this.sessionHelper.RestartScopeExe();
|
||||
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver());
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart scope exe.
|
||||
/// </summary>
|
||||
public void ExitScopeExe()
|
||||
{
|
||||
this.sessionHelper.ExitScopeExe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user