Skip to content

Commit

Permalink
[Testing] More UITests extension methods using an IQuery as parameter (
Browse files Browse the repository at this point in the history
…#25177)

* Implement current UITests extension methods using an identifier using a Query

* Changes to fix the build

* Allow queries in the WaitForElement and WaitForNoElement methods

* Changed comments
  • Loading branch information
jsuarezruiz authored Oct 20, 2024
1 parent 34399ff commit 70f1f76
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using Microsoft.Maui.Controls.Internals;

namespace Maui.Controls.Sample.Issues;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
121 changes: 120 additions & 1 deletion src/TestUtils/src/UITest.Appium/HelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ public static void Tap(this IApp app, string element)
app.FindElement(element).Click();
}

/// <summary>
/// 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
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Represents the query that identify an element by parameters such as type, text it contains or identifier.</param>
public static void Tap(this IApp app, IQuery query)
{
app.FindElement(query).Tap();
}

/// <summary>
/// Performs a mouse click on the matched element.
/// </summary>
Expand All @@ -34,6 +47,16 @@ public static void Click(this IApp app, string element)
app.FindElement(element).Click();
}

/// <summary>
/// Performs a mouse click on the matched element.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Represents the query that identify an element by parameters such as type, text it contains or identifier.</param>
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);
Expand Down Expand Up @@ -82,7 +105,7 @@ public static Rectangle GetRect(this IUIElement element)
}

/// <summary>
/// Enters text into the currently focused element.
/// Enters text into the element identified by the query.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="element">Target Element.</param>
Expand All @@ -94,6 +117,20 @@ public static void EnterText(this IApp app, string element, string text)
app.DismissKeyboard();
}

/// <summary>
/// Enters text into the element identified by the query.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="element">Target Element.</param>
/// <param name="query">Represents the query that identify an element by parameters such as type, text it contains or identifier.</param>
public static void EnterText(this IApp app, IQuery query, string text)
{
var appElement = app.FindElement(query);
appElement.SendKeys(text);
app.DismissKeyboard();
}


/// <summary>
/// Hides soft keyboard if present.
/// </summary>
Expand Down Expand Up @@ -148,6 +185,16 @@ public static void ClearText(this IApp app, string element)
app.FindElement(element).Clear();
}

/// <summary>
/// Clears text from the currently focused element.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Represents the query that identify an element by parameters such as type, text it contains or identifier.</param>
public static void ClearText(this IApp app, IQuery query)
{
app.FindElement(query).Clear();
}

/// <summary>
/// Performs a mouse click on the matched element.
/// </summary>
Expand Down Expand Up @@ -383,6 +430,16 @@ public static IReadOnlyCollection<string> GetAlertText(this IUIElement alertElem
return (IReadOnlyCollection<string>?)result.Value ?? Array.Empty<string>();
}

/// <summary>
/// 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.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="marked">Target Element.</param>
/// <param name="timeoutMessage">The message used in the TimeoutException.</param>
/// <param name="timeout">The TimeSpan to wait before failing.</param>
/// <param name="retryFrequency">The TimeSpan to wait between each query call to the app.</param>
/// <param name="postTimeout">The final TimeSpan to wait after the element has been found.</param>
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);
Expand All @@ -391,6 +448,33 @@ public static IReadOnlyCollection<string> GetAlertText(this IUIElement alertElem
return results;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Represents the query that identify an element by parameters such as type, text it contains or identifier.</param>
/// <param name="timeoutMessage">The message used in the TimeoutException.</param>
/// <param name="timeout">The TimeSpan to wait before failing.</param>
/// <param name="retryFrequency">The TimeSpan to wait between each query call to the app.</param>
/// <param name="postTimeout">The final TimeSpan to wait after the element has been found.</param>
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;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Entry point for the fluent API to specify the element.</param>
/// <param name="timeoutMessage">The message used in the TimeoutException.</param>
/// <param name="timeout">The TimeSpan to wait before failing.</param>
/// <param name="retryFrequency">The TimeSpan to wait between each query call to the app.</param>
public static IUIElement WaitForElement(
this IApp app,
Func<IUIElement?> query,
Expand All @@ -403,12 +487,47 @@ public static IUIElement WaitForElement(
return results;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="marked">Target Element.</param>
/// <param name="timeoutMessage">The message used in the TimeoutException.</param>
/// <param name="timeout">The TimeSpan to wait before failing.</param>
/// <param name="retryFrequency">The TimeSpan to wait between each query call to the app.</param>
/// <param name="postTimeout">The final TimeSpan to wait after the element has been found.</param>
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);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Represents the query that identify an element by parameters such as type, text it contains or identifier.</param>
/// <param name="timeoutMessage">The message used in the TimeoutException.</param>
/// <param name="timeout">The TimeSpan to wait before failing.</param>
/// <param name="retryFrequency">The TimeSpan to wait between each query call to the app.</param>
/// <param name="postTimeout">The final TimeSpan to wait after the element has been found.</param>
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);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <param name="query">Entry point for the fluent API to specify the element.</param>
/// <param name="timeoutMessage">The message used in the TimeoutException.</param>
/// <param name="timeout">The TimeSpan to wait before failing.</param>
/// <param name="retryFrequency">The TimeSpan to wait between each query call to the app.</param>
public static void WaitForNoElement(
this IApp app,
Func<IUIElement?> query,
Expand Down

0 comments on commit 70f1f76

Please sign in to comment.