diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Bugzilla/Bugzilla32148.cs b/src/Controls/tests/TestCases.HostApp/Issues/Bugzilla/Bugzilla32148.cs
index accc063bd5f7..0bf907eefbd1 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Bugzilla/Bugzilla32148.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Bugzilla/Bugzilla32148.cs
@@ -1,5 +1,6 @@
using System.Collections.ObjectModel;
using System.Linq.Expressions;
+using Microsoft.Maui.Controls.Internals;
namespace Maui.Controls.Sample.Issues;
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla32148.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla32148.cs
index d89eae100c05..c67d22d7fb9b 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla32148.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Bugzilla/Bugzilla32148.cs
@@ -12,14 +12,15 @@ public Bugzilla32148(TestDevice testDevice) : base(testDevice)
public override string Issue => " Pull to refresh hides the first item on a list view";
- // [Test]
- // [Category(UITestCategories.ListView)]
- // [FailsOnIOS]
- // public void Bugzilla32148Test()
- // {
- // App.WaitForElement("Contact0 LastName");
- // App.Tap("Search");
- // App.WaitForElement("Contact0 LastName");
- // App.Screenshot("For manual review, is the first cell visible?");
- // }
+ [Test]
+ [Category(UITestCategories.ListView)]
+ [FailsOnApple]
+ [FailsOnWindows("Sometimes the Teardown process fails after running the test.")]
+ public void Bugzilla32148Test()
+ {
+ App.WaitForNoElement("Contact0 LastName");
+ App.Tap(AppiumQuery.ByXPath("//*[@text='" + "Search" + "']"));
+ App.WaitForNoElement("Contact0 LastName");
+ App.Screenshot("For manual review, verify that the first cell is visible");
+ }
}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/UITestIgnoreAttributes.cs b/src/Controls/tests/TestCases.Shared.Tests/UITestIgnoreAttributes.cs
index 1f1674874fbc..b975bc9af228 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/UITestIgnoreAttributes.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/UITestIgnoreAttributes.cs
@@ -78,6 +78,28 @@ public FailsOnMac(string description) : base(description)
}
#endif
+#if IOS || MACCATALYST
+ public class FailsOnApple : IgnoreAttribute
+ {
+ public FailsOnApple() : base(nameof(FailsOnApple))
+ {
+ }
+ public FailsOnApple(string reason) : base(reason)
+ {
+ }
+ }
+#else
+ public class FailsOnApple : CategoryAttribute
+ {
+ public FailsOnApple() : base(nameof(FailsOnApple))
+ {
+ }
+ public FailsOnApple(string description) : base(description)
+ {
+ }
+ }
+#endif
+
#if WINDOWS
public class FailsOnWindows : IgnoreAttribute
{
diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
index 354607e5bd37..3437ab1f5d63 100644
--- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
+++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
@@ -24,6 +24,19 @@ public static void Tap(this IApp app, string element)
app.FindElement(element).Click();
}
+ ///
+ /// For desktop, this will perform a mouse click on the target element.
+ /// For mobile, this will tap the element.
+ /// This API works for all platforms whereas TapCoordinates currently doesn't work on Catalyst
+ /// https://github.com/dotnet/maui/issues/19754
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Represents the query that identify an element by parameters such as type, text it contains or identifier.
+ public static void Tap(this IApp app, IQuery query)
+ {
+ app.FindElement(query).Tap();
+ }
+
///
/// Performs a mouse click on the matched element.
///
@@ -34,6 +47,16 @@ public static void Click(this IApp app, string element)
app.FindElement(element).Click();
}
+ ///
+ /// Performs a mouse click on the matched element.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Represents the query that identify an element by parameters such as type, text it contains or identifier.
+ public static void Click(this IApp app, IQuery query)
+ {
+ app.FindElement(query).Click();
+ }
+
public static void RightClick(this IApp app, string element)
{
var uiElement = app.FindElement(element);
@@ -82,7 +105,7 @@ public static Rectangle GetRect(this IUIElement element)
}
///
- /// Enters text into the currently focused element.
+ /// Enters text into the element identified by the query.
///
/// Represents the main gateway to interact with an app.
/// Target Element.
@@ -94,6 +117,20 @@ public static void EnterText(this IApp app, string element, string text)
app.DismissKeyboard();
}
+ ///
+ /// Enters text into the element identified by the query.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Target Element.
+ /// Represents the query that identify an element by parameters such as type, text it contains or identifier.
+ public static void EnterText(this IApp app, IQuery query, string text)
+ {
+ var appElement = app.FindElement(query);
+ appElement.SendKeys(text);
+ app.DismissKeyboard();
+ }
+
+
///
/// Hides soft keyboard if present.
///
@@ -148,6 +185,16 @@ public static void ClearText(this IApp app, string element)
app.FindElement(element).Clear();
}
+ ///
+ /// Clears text from the currently focused element.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Represents the query that identify an element by parameters such as type, text it contains or identifier.
+ public static void ClearText(this IApp app, IQuery query)
+ {
+ app.FindElement(query).Clear();
+ }
+
///
/// Performs a mouse click on the matched element.
///
@@ -383,6 +430,16 @@ public static IReadOnlyCollection GetAlertText(this IUIElement alertElem
return (IReadOnlyCollection?)result.Value ?? Array.Empty();
}
+ ///
+ /// Wait function that will repeatly query the app until a matching element is found.
+ /// Throws a TimeoutException if no element is found within the time limit.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Target Element.
+ /// The message used in the TimeoutException.
+ /// The TimeSpan to wait before failing.
+ /// The TimeSpan to wait between each query call to the app.
+ /// The final TimeSpan to wait after the element has been found.
public static IUIElement WaitForElement(this IApp app, string marked, string timeoutMessage = "Timed out waiting for element...", TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
{
IUIElement result() => app.FindElement(marked);
@@ -391,6 +448,33 @@ public static IReadOnlyCollection GetAlertText(this IUIElement alertElem
return results;
}
+ ///
+ /// Wait function that will repeatly query the app until a matching element is found.
+ /// Throws a TimeoutException if no element is found within the time limit.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Represents the query that identify an element by parameters such as type, text it contains or identifier.
+ /// The message used in the TimeoutException.
+ /// The TimeSpan to wait before failing.
+ /// The TimeSpan to wait between each query call to the app.
+ /// The final TimeSpan to wait after the element has been found.
+ public static IUIElement WaitForElement(this IApp app, IQuery query, string timeoutMessage = "Timed out waiting for element...", TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
+ {
+ IUIElement result() => app.FindElement(query);
+ var results = WaitForAtLeastOne(result, timeoutMessage, timeout, retryFrequency);
+
+ return results;
+ }
+
+ ///
+ /// Wait function that will repeatly query the app until a matching element is found.
+ /// Throws a TimeoutException if no element is found within the time limit.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Entry point for the fluent API to specify the element.
+ /// The message used in the TimeoutException.
+ /// The TimeSpan to wait before failing.
+ /// The TimeSpan to wait between each query call to the app.
public static IUIElement WaitForElement(
this IApp app,
Func query,
@@ -403,12 +487,47 @@ public static IUIElement WaitForElement(
return results;
}
+ ///
+ /// Wait function that will repeatly query the app until a matching element is no longer found.
+ /// Throws a TimeoutException if the element is visible at the end of the time limit.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Target Element.
+ /// The message used in the TimeoutException.
+ /// The TimeSpan to wait before failing.
+ /// The TimeSpan to wait between each query call to the app.
+ /// The final TimeSpan to wait after the element has been found.
public static void WaitForNoElement(this IApp app, string marked, string timeoutMessage = "Timed out waiting for no element...", TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
{
IUIElement result() => app.FindElement(marked);
WaitForNone(result, timeoutMessage, timeout, retryFrequency);
}
+ ///
+ /// Wait function that will repeatly query the app until a matching element is no longer found.
+ /// Throws a TimeoutException if the element is visible at the end of the time limit.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Represents the query that identify an element by parameters such as type, text it contains or identifier.
+ /// The message used in the TimeoutException.
+ /// The TimeSpan to wait before failing.
+ /// The TimeSpan to wait between each query call to the app.
+ /// The final TimeSpan to wait after the element has been found.
+ public static void WaitForNoElement(this IApp app, IQuery query, string timeoutMessage = "Timed out waiting for no element...", TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
+ {
+ IUIElement result() => app.FindElement(query);
+ WaitForNone(result, timeoutMessage, timeout, retryFrequency);
+ }
+
+ ///
+ /// Wait function that will repeatly query the app until a matching element is no longer found.
+ /// Throws a TimeoutException if the element is visible at the end of the time limit.
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// Entry point for the fluent API to specify the element.
+ /// The message used in the TimeoutException.
+ /// The TimeSpan to wait before failing.
+ /// The TimeSpan to wait between each query call to the app.
public static void WaitForNoElement(
this IApp app,
Func query,