diff --git a/.pipelines/verifyDepsJsonLibraryVersions.ps1 b/.pipelines/verifyDepsJsonLibraryVersions.ps1
index 84397661d5..b91a8a566c 100644
--- a/.pipelines/verifyDepsJsonLibraryVersions.ps1
+++ b/.pipelines/verifyDepsJsonLibraryVersions.ps1
@@ -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
diff --git a/src/common/UITestAutomation/Element/Button.cs b/src/common/UITestAutomation/Element/Button.cs
index 44a7055fb2..b5915031be 100644
--- a/src/common/UITestAutomation/Element/Button.cs
+++ b/src/common/UITestAutomation/Element/Button.cs
@@ -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
{
///
diff --git a/src/common/UITestAutomation/Element/By.cs b/src/common/UITestAutomation/Element/By.cs
index 11217ff61a..5ccaf1eb06 100644
--- a/src/common/UITestAutomation/Element/By.cs
+++ b/src/common/UITestAutomation/Element/By.cs
@@ -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();
+ }
+
///
/// Creates a By object using the name attribute.
///
diff --git a/src/common/UITestAutomation/Element/Element.cs b/src/common/UITestAutomation/Element/Element.cs
index d4794507d6..f2f3d4ee29 100644
--- a/src/common/UITestAutomation/Element/Element.cs
+++ b/src/common/UITestAutomation/Element/Element.cs
@@ -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? driver;
internal void SetWindowsElement(WindowsElement windowsElement) => this.windowsElement = windowsElement;
@@ -43,7 +39,20 @@ namespace Microsoft.PowerToys.UITest
///
public string Text
{
- get { return GetAttribute("Value"); }
+ get { return this.windowsElement?.Text ?? string.Empty; }
+ }
+
+ ///
+ /// Gets a value indicating whether the UI element is Enabled or not.
+ ///
+ public bool Enabled
+ {
+ get { return this.windowsElement?.Enabled ?? false; }
+ }
+
+ public bool Selected
+ {
+ get { return this.windowsElement?.Selected ?? false; }
}
///
@@ -78,26 +87,19 @@ namespace Microsoft.PowerToys.UITest
get { return GetAttribute("ControlType"); }
}
- ///
- /// Checks if the UI element is enabled.
- ///
- /// True if the element is enabled; otherwise, false.
- public bool IsEnabled() => GetAttribute("IsEnabled") == "True";
-
- ///
- /// Checks if the UI element is selected.
- ///
- /// True if the element is selected; otherwise, false.
- public bool IsSelected() => GetAttribute("IsSelected") == "True";
-
///
/// Click the UI element.
///
- /// If true, performs a right-click; otherwise, performs a left-click.
+ /// If true, performs a right-click; otherwise, performs a left-click. Default value is false
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();
+ });
+ }
+
+ ///
+ /// Double Click the UI element.
+ ///
+ 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(
+ var foundElement = FindHelper.Find(
() =>
{
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(
+ var foundElements = FindHelper.FindAll(
() =>
{
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
@@ -173,13 +194,24 @@ namespace Microsoft.PowerToys.UITest
///
/// Simulates a manual operation on the element.
///
- private void PerformAction(Action action)
+ /// The action to perform on the element.
+ /// The number of milliseconds to wait before the action. Default value is 100 ms
+ /// The number of milliseconds to wait after the action. Default value is 100 ms
+ protected void PerformAction(Action 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();
+ }
}
}
}
diff --git a/src/common/UITestAutomation/Element/TextBox.cs b/src/common/UITestAutomation/Element/TextBox.cs
new file mode 100644
index 0000000000..e427f7c9d3
--- /dev/null
+++ b/src/common/UITestAutomation/Element/TextBox.cs
@@ -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
+{
+ ///
+ /// Represents a textbox in the UI test environment.
+ ///
+ public class TextBox : Element
+ {
+ ///
+ /// Sets the text of the textbox.
+ ///
+ /// The text to set.
+ /// A value indicating whether to clear the text before setting it. Default value is true
+ /// The current TextBox instance.
+ 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;
+ }
+ }
+}
diff --git a/src/common/UITestAutomation/Element/ToggleSwitch.cs b/src/common/UITestAutomation/Element/ToggleSwitch.cs
new file mode 100644
index 0000000000..b2a7f1b607
--- /dev/null
+++ b/src/common/UITestAutomation/Element/ToggleSwitch.cs
@@ -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
+{
+ ///
+ /// Represents a ToggleSwitch in the UI test environment.
+ ///
+ public class ToggleSwitch : Button
+ {
+ ///
+ /// Gets a value indicating whether the ToggleSwitch is on.
+ ///
+ public bool IsOn
+ {
+ get
+ {
+ return this.Selected;
+ }
+ }
+
+ ///
+ /// Sets the ToggleSwitch to the specified value.
+ ///
+ /// A value indicating whether the ToggleSwitch should be active. Default is true
+ /// The current ToggleSwitch instance.
+ public ToggleSwitch Toggle(bool value = true)
+ {
+ if (this.IsOn != value)
+ {
+ // Toggle the switch
+ this.Click();
+ }
+
+ return this;
+ }
+ }
+}
diff --git a/src/common/UITestAutomation/Element/Window.cs b/src/common/UITestAutomation/Element/Window.cs
index 558e0cb0b2..eceb1fcf47 100644
--- a/src/common/UITestAutomation/Element/Window.cs
+++ b/src/common/UITestAutomation/Element/Window.cs
@@ -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
{
///
diff --git a/src/common/UITestAutomation/Element/FindElementHelper.cs b/src/common/UITestAutomation/FindHelper.cs
similarity index 86%
rename from src/common/UITestAutomation/Element/FindElementHelper.cs
rename to src/common/UITestAutomation/FindHelper.cs
index 4ed42a9639..241aa6b339 100644
--- a/src/common/UITestAutomation/Element/FindElementHelper.cs
+++ b/src/common/UITestAutomation/FindHelper.cs
@@ -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
///
/// Helper class for finding elements.
///
- internal static class FindElementHelper
+ internal static class FindHelper
{
public static T Find(Func findElementFunc, WindowsDriver? 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;
diff --git a/src/common/UITestAutomation/Session.cs b/src/common/UITestAutomation/Session.cs
index 2e21cc392e..7071f6d2e2 100644
--- a/src/common/UITestAutomation/Session.cs
+++ b/src/common/UITestAutomation/Session.cs
@@ -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(
+ var foundElement = FindHelper.Find(
() =>
{
var element = this.WindowsDriver.FindElement(by.ToSeleniumBy());
@@ -65,21 +59,20 @@ namespace Microsoft.PowerToys.UITest
/// The selector to find the elements.
/// The timeout in milliseconds (default is 3000).
/// A read-only collection of the found elements.
- public ReadOnlyCollection? FindAll(By by, int timeoutMS = 3000)
+ public ReadOnlyCollection FindAll(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(
+ var foundElements = FindHelper.FindAll(
() =>
{
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(new List());
}
///
@@ -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(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), appCapabilities);
diff --git a/src/common/UITestAutomation/UITestAutomation.csproj b/src/common/UITestAutomation/UITestAutomation.csproj
index 0f2e53681d..0da17d85b8 100644
--- a/src/common/UITestAutomation/UITestAutomation.csproj
+++ b/src/common/UITestAutomation/UITestAutomation.csproj
@@ -1,8 +1,9 @@
+
+
Library
- net9.0
enable
enable
true
@@ -15,7 +16,4 @@
-
-
-
diff --git a/src/common/UITestAutomation/UITestBase.cs b/src/common/UITestAutomation/UITestBase.cs
index ac69ad04b1..4ce432f23a 100644
--- a/src/common/UITestAutomation/UITestBase.cs
+++ b/src/common/UITestAutomation/UITestBase.cs
@@ -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();
}
+ ///
+ /// Finds an element by selector.
+ /// Shortcut for this.Session.Find(by, timeoutMS)
+ ///
+ /// The class of the element, should be Element or its derived class.
+ /// The selector to find the element.
+ /// The timeout in milliseconds (default is 3000).
+ /// The found element.
+ protected T Find(By by, int timeoutMS = 3000)
+ where T : Element, new()
+ {
+ return this.Session.Find(by, timeoutMS);
+ }
+
+ ///
+ /// Finds all elements by selector.
+ /// Shortcut for this.Session.FindAll(by, timeoutMS)
+ ///
+ /// The class of the elements, should be Element or its derived class.
+ /// The selector to find the elements.
+ /// The timeout in milliseconds (default is 3000).
+ /// A read-only collection of the found elements.
+ protected ReadOnlyCollection FindAll(By by, int timeoutMS = 3000)
+ where T : Element, new()
+ {
+ return this.Session.FindAll(by, timeoutMS);
+ }
+
///
/// Nested class for test initialization.
///