mirror of
https://github.com/microsoft/PowerToys
synced 2025-08-22 01:58:04 +00:00
Update UI-Test Automation Framework (#37597)
* Improve UITest Automation * Improve UITest Automation * Exclude all UI-Test projects instead of just fancyZone UITest * Exclude all UI-Test projects instead of just fancyZone UITest * Fix code-style
This commit is contained in:
parent
3970e89ee7
commit
83cea39b66
@ -15,8 +15,8 @@ Param(
|
||||
$referencedFileVersionsPerDll = @{}
|
||||
$totalFailures = 0
|
||||
|
||||
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones*,MouseJump.Common.UnitTests*,*.FuzzTests* | ForEach-Object {
|
||||
# Temporarily exclude FancyZones UI tests because of Appium.WebDriver dependencies
|
||||
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude *UITests*,MouseJump.Common.UnitTests*,*.FuzzTests* | ForEach-Object {
|
||||
# Temporarily exclude All UI-Test, Fuzzer-Test projects because of Appium.WebDriver dependencies
|
||||
$depsJsonFullFileName = $_.FullName
|
||||
$depsJsonFileName = $_.Name
|
||||
$depsJson = Get-Content $depsJsonFullFileName | ConvertFrom-Json
|
||||
|
@ -2,13 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using OpenQA.Selenium.Remote;
|
||||
using OpenQA.Selenium.Support.Events;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -18,6 +18,12 @@ namespace Microsoft.PowerToys.UITest
|
||||
this.by = by;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// override ToString to return detailed debugging content provided by OpenQA.Selenium.By
|
||||
return this.by.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a By object using the name attribute.
|
||||
/// </summary>
|
||||
|
@ -3,16 +3,11 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using OpenQA.Selenium.Remote;
|
||||
using OpenQA.Selenium.Support.Events;
|
||||
using static Microsoft.PowerToys.UITest.UITestBase;
|
||||
|
||||
[assembly: InternalsVisibleTo("Session")]
|
||||
|
||||
@ -24,6 +19,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
public class Element
|
||||
{
|
||||
private WindowsElement? windowsElement;
|
||||
|
||||
private WindowsDriver<WindowsElement>? driver;
|
||||
|
||||
internal void SetWindowsElement(WindowsElement windowsElement) => this.windowsElement = windowsElement;
|
||||
@ -43,7 +39,20 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get { return GetAttribute("Value"); }
|
||||
get { return this.windowsElement?.Text ?? string.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UI element is Enabled or not.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get { return this.windowsElement?.Enabled ?? false; }
|
||||
}
|
||||
|
||||
public bool Selected
|
||||
{
|
||||
get { return this.windowsElement?.Selected ?? false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -78,26 +87,19 @@ namespace Microsoft.PowerToys.UITest
|
||||
get { return GetAttribute("ControlType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the UI element is enabled.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is enabled; otherwise, false.</returns>
|
||||
public bool IsEnabled() => GetAttribute("IsEnabled") == "True";
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the UI element is selected.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is selected; otherwise, false.</returns>
|
||||
public bool IsSelected() => GetAttribute("IsSelected") == "True";
|
||||
|
||||
/// <summary>
|
||||
/// Click the UI element.
|
||||
/// </summary>
|
||||
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click.</param>
|
||||
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click. Default value is false</param>
|
||||
public void Click(bool rightClick = false)
|
||||
{
|
||||
PerformAction(actions =>
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
actions.MoveToElement(windowElement);
|
||||
|
||||
// Move 2by2 offset to make click more stable instead of click on the border of the element
|
||||
actions.MoveByOffset(2, 2);
|
||||
|
||||
if (rightClick)
|
||||
{
|
||||
actions.ContextClick();
|
||||
@ -106,6 +108,25 @@ namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
actions.Click();
|
||||
}
|
||||
|
||||
actions.Build().Perform();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Double Click the UI element.
|
||||
/// </summary>
|
||||
public void DoubleClick()
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
actions.MoveToElement(windowElement);
|
||||
|
||||
// Move 2by2 offset to make click more stable instead of click on the border of the element
|
||||
actions.MoveByOffset(2, 2);
|
||||
|
||||
actions.DoubleClick();
|
||||
actions.Build().Perform();
|
||||
});
|
||||
}
|
||||
|
||||
@ -133,7 +154,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
where T : Element, new()
|
||||
{
|
||||
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
|
||||
var foundElement = FindElementHelper.Find<T, AppiumWebElement>(
|
||||
var foundElement = FindHelper.Find<T, AppiumWebElement>(
|
||||
() =>
|
||||
{
|
||||
var element = this.windowsElement.FindElement(by.ToSeleniumBy());
|
||||
@ -157,7 +178,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
where T : Element, new()
|
||||
{
|
||||
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
|
||||
var foundElements = FindElementHelper.FindAll<T, AppiumWebElement>(
|
||||
var foundElements = FindHelper.FindAll<T, AppiumWebElement>(
|
||||
() =>
|
||||
{
|
||||
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
|
||||
@ -173,13 +194,24 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <summary>
|
||||
/// Simulates a manual operation on the element.
|
||||
/// </summary>
|
||||
private void PerformAction(Action<Actions> action)
|
||||
/// <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 100 ms</param>
|
||||
/// <param name="msPostAction">The number of milliseconds to wait after the action. Default value is 100 ms</param>
|
||||
protected void PerformAction(Action<Actions, WindowsElement> action, int msPreAction = 100, int msPostAction = 100)
|
||||
{
|
||||
var element = this.windowsElement;
|
||||
if (msPreAction > 0)
|
||||
{
|
||||
Task.Delay(msPreAction).Wait();
|
||||
}
|
||||
|
||||
var windowElement = this.windowsElement!;
|
||||
Actions actions = new Actions(this.driver);
|
||||
actions.MoveToElement(element);
|
||||
action(actions);
|
||||
actions.Build().Perform();
|
||||
action(actions, windowElement);
|
||||
|
||||
if (msPostAction > 0)
|
||||
{
|
||||
Task.Delay(msPostAction).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
40
src/common/UITestAutomation/Element/TextBox.cs
Normal file
40
src/common/UITestAutomation/Element/TextBox.cs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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 OpenQA.Selenium;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a textbox in the UI test environment.
|
||||
/// </summary>
|
||||
public class TextBox : Element
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the text of the textbox.
|
||||
/// </summary>
|
||||
/// <param name="value">The text to set.</param>
|
||||
/// <param name="clearText">A value indicating whether to clear the text before setting it. Default value is true</param>
|
||||
/// <returns>The current TextBox instance.</returns>
|
||||
public TextBox SetText(string value, bool clearText = true)
|
||||
{
|
||||
if (clearText)
|
||||
{
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
// select all text and delete it
|
||||
windowElement.SendKeys(Keys.Control + "a");
|
||||
windowElement.SendKeys(Keys.Delete);
|
||||
});
|
||||
}
|
||||
|
||||
PerformAction((actions, windowElement) =>
|
||||
{
|
||||
windowElement.SendKeys(value);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
41
src/common/UITestAutomation/Element/ToggleSwitch.cs
Normal file
41
src/common/UITestAutomation/Element/ToggleSwitch.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a ToggleSwitch in the UI test environment.
|
||||
/// </summary>
|
||||
public class ToggleSwitch : Button
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the ToggleSwitch is on.
|
||||
/// </summary>
|
||||
public bool IsOn
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Selected;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ToggleSwitch to the specified value.
|
||||
/// </summary>
|
||||
/// <param name="value">A value indicating whether the ToggleSwitch should be active. Default is true</param>
|
||||
/// <returns>The current ToggleSwitch instance.</returns>
|
||||
public ToggleSwitch Toggle(bool value = true)
|
||||
{
|
||||
if (this.IsOn != value)
|
||||
{
|
||||
// Toggle the switch
|
||||
this.Click();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,13 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using OpenQA.Selenium.Remote;
|
||||
using OpenQA.Selenium.Support.Events;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -2,16 +2,9 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
|
||||
[assembly: InternalsVisibleTo("Element")]
|
||||
@ -22,7 +15,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <summary>
|
||||
/// Helper class for finding elements.
|
||||
/// </summary>
|
||||
internal static class FindElementHelper
|
||||
internal static class FindHelper
|
||||
{
|
||||
public static T Find<T, TW>(Func<TW> findElementFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
|
||||
where T : Element, new()
|
||||
@ -51,7 +44,12 @@ namespace Microsoft.PowerToys.UITest
|
||||
Assert.IsNotNull(element, $"New Element {typeof(T).Name} error: element is null.");
|
||||
|
||||
T newElement = new T();
|
||||
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(timeoutMS);
|
||||
if (timeoutMS > 0)
|
||||
{
|
||||
// Only set timeout if it is positive value
|
||||
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(timeoutMS);
|
||||
}
|
||||
|
||||
newElement.SetSession(driver);
|
||||
newElement.SetWindowsElement(element);
|
||||
return newElement;
|
@ -2,17 +2,11 @@
|
||||
// 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.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
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;
|
||||
|
||||
namespace Microsoft.PowerToys.UITest
|
||||
{
|
||||
@ -45,7 +39,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
where T : Element, new()
|
||||
{
|
||||
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
|
||||
var foundElement = FindElementHelper.Find<T, WindowsElement>(
|
||||
var foundElement = FindHelper.Find<T, WindowsElement>(
|
||||
() =>
|
||||
{
|
||||
var element = this.WindowsDriver.FindElement(by.ToSeleniumBy());
|
||||
@ -65,21 +59,20 @@ namespace Microsoft.PowerToys.UITest
|
||||
/// <param name="by">The selector to find the elements.</param>
|
||||
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
|
||||
/// <returns>A read-only collection of the found elements.</returns>
|
||||
public ReadOnlyCollection<T>? FindAll<T>(By by, int timeoutMS = 3000)
|
||||
public ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
|
||||
where T : Element, new()
|
||||
{
|
||||
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
|
||||
var foundElements = FindElementHelper.FindAll<T, WindowsElement>(
|
||||
var foundElements = FindHelper.FindAll<T, WindowsElement>(
|
||||
() =>
|
||||
{
|
||||
var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy());
|
||||
Assert.IsTrue(elements.Count > 0, $"Elements not found using selector: {by}");
|
||||
return elements;
|
||||
},
|
||||
this.WindowsDriver,
|
||||
timeoutMS);
|
||||
|
||||
return foundElements;
|
||||
return foundElements ?? new ReadOnlyCollection<T>(new List<T>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -110,6 +103,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
SetForegroundWindow(windowHandle);
|
||||
var hexWindowHandle = windowHandle.ToString("x");
|
||||
var appCapabilities = new AppiumOptions();
|
||||
|
||||
appCapabilities.AddAdditionalCapability("appTopLevelWindow", hexWindowHandle);
|
||||
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
|
||||
this.WindowsDriver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), appCapabilities);
|
||||
|
@ -1,8 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishAot>true</PublishAot>
|
||||
@ -15,7 +16,4 @@
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -2,17 +2,13 @@
|
||||
// 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
|
||||
{
|
||||
@ -37,6 +33,34 @@ namespace Microsoft.PowerToys.UITest
|
||||
this.testInit.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an element by selector.
|
||||
/// Shortcut for this.Session.Find<T>(by, timeoutMS)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
|
||||
/// <param name="by">The selector to find the element.</param>
|
||||
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
|
||||
/// <returns>The found element.</returns>
|
||||
protected T Find<T>(By by, int timeoutMS = 3000)
|
||||
where T : Element, new()
|
||||
{
|
||||
return this.Session.Find<T>(by, timeoutMS);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all elements by selector.
|
||||
/// Shortcut for this.Session.FindAll<T>(by, timeoutMS)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
|
||||
/// <param name="by">The selector to find the elements.</param>
|
||||
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
|
||||
/// <returns>A read-only collection of the found elements.</returns>
|
||||
protected ReadOnlyCollection<T> FindAll<T>(By by, int timeoutMS = 3000)
|
||||
where T : Element, new()
|
||||
{
|
||||
return this.Session.FindAll<T>(by, timeoutMS);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nested class for test initialization.
|
||||
/// </summary>
|
||||
|
Loading…
x
Reference in New Issue
Block a user