Skip to content

Commit

Permalink
[Testing] More Appium actions (dotnet#25569)
Browse files Browse the repository at this point in the history
* More Appium actions

* Fixed build

* Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Update src/TestUtils/src/UITest.Appium/HelperExtensions.cs

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

---------

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
  • Loading branch information
jsuarezruiz and jfversluis authored Oct 31, 2024
1 parent 8e603a4 commit 2077463
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 24 deletions.
17 changes: 17 additions & 0 deletions src/TestUtils/src/UITest.Appium/Actions/AppiumDeviceActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class AppiumDeviceActions : ICommandExecutionGroup
{
const string LockCommand = "lock";
const string UnlockCommand = "unlock";
const string IsLockedCommand = "isLocked";
const string StartRecordingScreenCommand = "startRecordingScreen";
const string StopRecordingScreenCommand = "stopRecordingScreen";

Expand All @@ -18,6 +19,7 @@ public class AppiumDeviceActions : ICommandExecutionGroup
{
LockCommand,
UnlockCommand,
IsLockedCommand,
StartRecordingScreenCommand,
StopRecordingScreenCommand,
};
Expand All @@ -38,6 +40,7 @@ public CommandResponse Execute(string commandName, IDictionary<string, object> p
{
LockCommand => Lock(parameters),
UnlockCommand => Unlock(parameters),
IsLockedCommand => IsLocked(parameters),
StartRecordingScreenCommand => StartRecordingScreen(parameters),
StopRecordingScreenCommand => StopRecordingScreen(parameters),
_ => CommandResponse.FailedEmptyResponse,
Expand Down Expand Up @@ -80,6 +83,20 @@ CommandResponse Unlock(IDictionary<string, object> parameters)
return CommandResponse.FailedEmptyResponse;
}

CommandResponse IsLocked(IDictionary<string, object> parameters)
{
if (_appiumApp.Driver is AndroidDriver androidDriver)
{
return new CommandResponse(androidDriver.IsLocked(), CommandResponseResult.Success);
}
if (_appiumApp.Driver is IOSDriver iOSDriver)
{
return new CommandResponse(iOSDriver.IsLocked(), CommandResponseResult.Success);
}

return CommandResponse.FailedEmptyResponse;
}

CommandResponse StartRecordingScreen(IDictionary<string, object> parameters)
{
if (_appiumApp.Driver is AndroidDriver androidDriver)
Expand Down
59 changes: 58 additions & 1 deletion src/TestUtils/src/UITest.Appium/Actions/AppiumGeneralActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ public class AppiumGeneralActions : ICommandExecutionGroup
{
const string GetAttributeCommand = "getAttribute";
const string GetRectCommand = "getRect";
const string GetSelectedCommand = "getSelected";
const string GetDisplayedCommand = "getDisplayed";
const string GetEnabledCommand = "getEnabled";

readonly List<string> _commands = new()
{
GetAttributeCommand,
GetRectCommand,
GetSelectedCommand,
GetDisplayedCommand,
GetEnabledCommand,
};

public bool IsCommandSupported(string commandName)
Expand All @@ -25,6 +31,9 @@ public CommandResponse Execute(string commandName, IDictionary<string, object> p
{
GetAttributeCommand => GetAttribute(parameters),
GetRectCommand => GetRect(parameters),
GetSelectedCommand => GetSelected(parameters),
GetDisplayedCommand => GetDisplayed(parameters),
GetEnabledCommand => GetEnabled(parameters),
_ => CommandResponse.FailedEmptyResponse,
};
}
Expand All @@ -35,7 +44,7 @@ CommandResponse GetRect(IDictionary<string, object> parameters)

if (element is AppiumElement appiumElement)
{
return new CommandResponse(appiumElement.Rect, CommandResponseResult.Success);
return new CommandResponse(appiumElement, CommandResponseResult.Success);
}
else if (element is AppiumDriverElement driverElement)
{
Expand All @@ -59,5 +68,53 @@ CommandResponse GetAttribute(IDictionary<string, object> parameters)
}
return CommandResponse.FailedEmptyResponse;
}

CommandResponse GetSelected(IDictionary<string, object> parameters)
{
var element = parameters["element"];

if (element is AppiumElement appiumElement)
{
return new CommandResponse(appiumElement, CommandResponseResult.Success);
}
else if (element is AppiumDriverElement driverElement)
{
return new CommandResponse(driverElement.AppiumElement.Selected, CommandResponseResult.Success);
}

return CommandResponse.FailedEmptyResponse;
}

CommandResponse GetDisplayed(IDictionary<string, object> parameters)
{
var element = parameters["element"];

if (element is AppiumElement appiumElement)
{
return new CommandResponse(appiumElement, CommandResponseResult.Success);
}
else if (element is AppiumDriverElement driverElement)
{
return new CommandResponse(driverElement.AppiumElement.Displayed, CommandResponseResult.Success);
}

return CommandResponse.FailedEmptyResponse;
}

CommandResponse GetEnabled(IDictionary<string, object> parameters)
{
var element = parameters["element"];

if (element is AppiumElement appiumElement)
{
return new CommandResponse(appiumElement, CommandResponseResult.Success);
}
else if (element is AppiumDriverElement driverElement)
{
return new CommandResponse(driverElement.AppiumElement.Enabled, CommandResponseResult.Success);
}

return CommandResponse.FailedEmptyResponse;
}
}
}
19 changes: 16 additions & 3 deletions src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Appium.Windows;
using UITest.Core;

namespace UITest.Appium
Expand All @@ -12,6 +11,7 @@ public class AppiumLifecycleActions : ICommandExecutionGroup
const string ResetAppCommand = "resetApp";
const string CloseAppCommand = "closeApp";
const string BackCommand = "back";
const string RefreshCommand = "refresh";

protected readonly AppiumApp _app;

Expand All @@ -22,7 +22,8 @@ public class AppiumLifecycleActions : ICommandExecutionGroup
BackgroundAppCommand,
ResetAppCommand,
CloseAppCommand,
BackCommand
BackCommand,
RefreshCommand
};

public AppiumLifecycleActions(AppiumApp app)
Expand All @@ -45,6 +46,7 @@ public CommandResponse Execute(string commandName, IDictionary<string, object> p
ResetAppCommand => ResetApp(parameters),
CloseAppCommand => CloseApp(parameters),
BackCommand => Back(parameters),
RefreshCommand => Refresh(parameters),
_ => CommandResponse.FailedEmptyResponse,
};
}
Expand Down Expand Up @@ -186,5 +188,16 @@ CommandResponse Back(IDictionary<string, object> parameters)
return CommandResponse.FailedEmptyResponse;
}
}

CommandResponse Refresh(IDictionary<string, object> parameters)
{
if (_app?.Driver is null)
return CommandResponse.FailedEmptyResponse;

// Refresh the current page.
_app.Driver.Navigate().Refresh();

return CommandResponse.SuccessEmptyResponse;
}
}
}
124 changes: 104 additions & 20 deletions src/TestUtils/src/UITest.Appium/HelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,66 @@ public static Rectangle GetRect(this IUIElement element)
throw new InvalidOperationException($"Could not get Rect of element");
}

/// <summary>
/// Determine if a form or form-like element (checkbox, select, etc...) is selected.
/// </summary>
/// <param name="element">Target Element.</param>
/// <returns>Whether the element is selected (boolean).</returns>
public static bool IsSelected(this IUIElement element)
{
var response = element.Command.Execute("getSelected", new Dictionary<string, object>()
{
{ "element", element },
});

if (response?.Value != null)
{
return (bool)response.Value;
}

throw new InvalidOperationException($"Could not get Selected of element");
}

/// <summary>
/// Determine if an element is currently displayed.
/// </summary>
/// <param name="element">Target Element.</param>
/// <returns>Whether the element is displayed (boolean).</returns>
public static bool IsDisplayed(this IUIElement element)
{
var response = element.Command.Execute("getDisplayed", new Dictionary<string, object>()
{
{ "element", element },
});

if (response?.Value != null)
{
return (bool)response.Value;
}

throw new InvalidOperationException($"Could not get Displayed of element");
}

/// <summary>
/// Determine if an element is currently enabled.
/// </summary>
/// <param name="element">Target Element.</param>
/// <returns>Whether the element is enabled (boolean).</returns>
public static bool IsEnabled(this IUIElement element)
{
var response = element.Command.Execute("getEnabled", new Dictionary<string, object>()
{
{ "element", element },
});

if (response?.Value != null)
{
return (bool)response.Value;
}

throw new InvalidOperationException($"Could not get Enabled of element");
}

/// <summary>
/// Enters text into the element identified by the query.
/// </summary>
Expand Down Expand Up @@ -1270,6 +1330,15 @@ public static void Back(this IApp app)
app.CommandExecutor.Execute("back", ImmutableDictionary<string, object>.Empty);
}

/// <summary>
/// Refresh the current page.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
public static void Refresh(this IApp app)
{
app.CommandExecutor.Execute("refresh", ImmutableDictionary<string, object>.Empty);
}

/// <summary>
/// Return the AppId of the running app. This is used inside any appium command that want the app id
/// </summary>
Expand Down Expand Up @@ -1371,9 +1440,9 @@ public static bool IsFocused(this IApp app, string id)
/// <exception cref="InvalidOperationException">Lock is only supported on <see cref="AppiumAndroidApp"/>.</exception>
public static void Lock(this IApp app)
{
if (app is not AppiumAndroidApp)
if (app is not AppiumAndroidApp && app is not AppiumIOSApp)
{
throw new InvalidOperationException($"Lock is only supported on AppiumAndroidApp");
throw new InvalidOperationException($"Lock is only supported on AppiumAndroidApp and AppiumIOSApp");
}

app.CommandExecutor.Execute("lock", ImmutableDictionary<string, object>.Empty);
Expand All @@ -1387,14 +1456,45 @@ public static void Lock(this IApp app)
/// <exception cref="InvalidOperationException">Unlock is only supported on <see cref="AppiumAndroidApp"/>.</exception>
public static void Unlock(this IApp app)
{
if (app is not AppiumAndroidApp)
if (app is not AppiumAndroidApp && app is not AppiumIOSApp)
{
throw new InvalidOperationException($"Unlock is only supported on AppiumAndroidApp");
throw new InvalidOperationException($"Unlock is only supported on AppiumAndroidApp and AppiumIOSApp");
}

app.CommandExecutor.Execute("unlock", ImmutableDictionary<string, object>.Empty);
}

/// <summary>
/// Check whether the device is locked or not.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
public static bool IsLocked(this IApp app)
{
if (app is not AppiumAndroidApp && app is not AppiumIOSApp)
{
throw new InvalidOperationException($"IsLocked is only supported on AppiumAndroidApp and AppiumIOSApp");
}
var response = app.CommandExecutor.Execute("isLocked", new Dictionary<string, object>());

var responseValue = response?.Value ?? false;

return (bool)responseValue;
}

/// <summary>
/// Perform a shake action on the device.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
public static void Shake(this IApp app)
{
if (app is not AppiumAndroidApp)
{
throw new InvalidOperationException($"Shake is only supported on AppiumAndroidApp");
}

app.CommandExecutor.Execute("shake", ImmutableDictionary<string, object>.Empty);
}

/// <summary>
/// Start recording screen.
/// Functionality that's only available on Android, iOS and Windows.
Expand Down Expand Up @@ -1459,22 +1559,6 @@ public static void ToggleWifi(this IApp app)
app.CommandExecutor.Execute("toggleWifi", ImmutableDictionary<string, object>.Empty);
}

/// <summary>
/// Simulate the device shaking.
/// Functionality that's only available on iOS.
/// </summary>
/// <param name="app">Represents the main gateway to interact with an app.</param>
/// <exception cref="InvalidOperationException">ToggleWifi is only supported on <see cref="AppiumAndroidApp"/>.</exception>
public static void Shake(this IApp app)
{
if (app is not AppiumIOSApp)
{
throw new InvalidOperationException($"Shake is only supported on AppiumIOSApp");
}

app.CommandExecutor.Execute("shake", ImmutableDictionary<string, object>.Empty);
}

/// <summary>
/// Gets the information of the system state which is supported to read as like cpu, memory, network traffic, and battery.
/// Functionality that's only available on Android.
Expand Down

0 comments on commit 2077463

Please sign in to comment.