Skip to content

BREAKING CHANGE: use extension commands for deprecated endpoints #939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 49 commits into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
66e9efc
feat: use mobile:background
KazuCocoa May 5, 2025
bb601d3
pdate clipboard stuff
KazuCocoa May 11, 2025
2542436
simplify a bit
KazuCocoa May 11, 2025
59a7989
turn
KazuCocoa May 11, 2025
06ca05d
query app state
KazuCocoa May 11, 2025
60f44e5
add /se
KazuCocoa May 15, 2025
7e0c572
update for device time
KazuCocoa May 15, 2025
36c8977
update for system bar
KazuCocoa May 15, 2025
8ff9220
device density command
KazuCocoa May 15, 2025
aecf529
gps
KazuCocoa May 15, 2025
83cbd42
replace android smulator stuff
KazuCocoa May 15, 2025
c990f61
activities
KazuCocoa May 15, 2025
efe2039
pressKeys
KazuCocoa May 15, 2025
093bc24
long press
KazuCocoa May 15, 2025
fcecd7f
keyboards
KazuCocoa May 16, 2025
a976847
touch id etc
KazuCocoa May 16, 2025
e247c7d
change rest
KazuCocoa May 16, 2025
e6d66b4
use Array.Empty<object>
KazuCocoa May 17, 2025
8fdec23
use proper args
KazuCocoa May 17, 2025
e4cef26
adjust arguments
KazuCocoa May 17, 2025
bce7f86
remove ToggleAirplaneMode
KazuCocoa May 17, 2025
0b800b9
remove ToggleData
KazuCocoa May 17, 2025
1aa6456
remove ToggleWifi
KazuCocoa May 17, 2025
a07868e
remove connection type stuff
KazuCocoa May 17, 2025
e90ea69
remove EndTestCoverage
KazuCocoa May 17, 2025
daa2180
update start activity
KazuCocoa May 17, 2025
1a0a020
Update test/integration/Android/Device/App/AppTests.cs
KazuCocoa May 19, 2025
19b5a35
replace windows stuff
KazuCocoa May 19, 2025
fe1f83a
Merge branch 'extention-commands' of github.com:appium/dotnet-client …
KazuCocoa May 19, 2025
28f77ea
update readme and rmeove redundant spaces
KazuCocoa May 19, 2025
da7f31d
fix review
KazuCocoa May 19, 2025
3b2a205
tweak review
KazuCocoa May 19, 2025
ad5834c
fix typo
KazuCocoa May 19, 2025
823e020
cleanup a bit more
KazuCocoa May 19, 2025
862f7f5
tweak the readme
KazuCocoa May 19, 2025
e62c602
Apply suggestions from code review
KazuCocoa May 19, 2025
b200293
update readme
KazuCocoa May 25, 2025
54fdc79
modify the readme format
KazuCocoa May 25, 2025
bdb386d
add xml doc
KazuCocoa May 25, 2025
cfbf58e
Merge branch 'extention-commands' of github.com:appium/dotnet-client …
KazuCocoa May 25, 2025
acab15b
extract to private methods for press key code stuff
KazuCocoa May 25, 2025
bae40c6
fix table format
KazuCocoa May 25, 2025
47c51ea
add xml doc
KazuCocoa May 25, 2025
b99efa7
Update README.md
KazuCocoa May 25, 2025
f109cc0
applyl suggestion
KazuCocoa May 25, 2025
93ca05b
Merge branch 'extention-commands' of github.com:appium/dotnet-client …
KazuCocoa May 25, 2025
94dd159
update readme
KazuCocoa May 28, 2025
dd58fe7
use shared method
KazuCocoa May 28, 2025
3812d96
fix typo
KazuCocoa May 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

[![License](https://img.shields.io/badge/License-Apache_2.0-lightblue.svg)](https://opensource.org/licenses/Apache-2.0)

This driver is an extension of the [Selenium](http://docs.seleniumhq.org/) C# client. It has
This driver is an extension of the [Selenium](http://docs.seleniumhq.org/) C# client. It has
all the functionalities of the regular driver, but add Appium-specific methods on top of this.

## Compatibility Matrix

The Appium .NET Client depends on [Selenium .NET binding](https://www.nuget.org/packages/Selenium.WebDriver), thus the Selenium .NET binding update might affect the Appium .NET Client behavior.
The Appium .NET Client depends on [Selenium .NET binding](https://www.nuget.org/packages/Selenium.WebDriver), thus the Selenium .NET binding update might affect the Appium .NET Client behavior.
For example, some changes in the Selenium binding could break the Appium client.

|Appium .NET Client| Selenium Binding | .NET Version |
Expand All @@ -27,8 +27,23 @@ For example, some changes in the Selenium binding could break the Appium client.
|`5.0.0` |`4.0.0` - `4.22.0` | .NET 6.0, .NET Framework 4.8 |
|`4.4.5` |`3.141.0` |.NET Standard 2.0, .NET Framework 4.8 |


## v8
To keep compatibility with Appium v3, most deprecated endpoint method calls have been replaced with compatible [extension command](https://appium.io/docs/en/latest/guides/execute-methods/) with [this PR](https://github.com/appium/dotnet-client/pull/939). Old drivers which still haven't implemented extention commands might not have proper implementation. Then, you will need to update Appium driver versions first.

| Removed Method or argument | Note |
|------------|----------------------|-------|
| `ToggleAirplaneMode()` | Change with `airplaneMode` parameter in [mobile:setConnectivity](https://github.com/appium/appium-uiautomator2-driver#mobile-setconnectivity) |
| `ToggleData()` | Change with `data` parameter in [mobile:setConnectivity](https://github.com/appium/appium-uiautomator2-driver#mobile-setconnectivity) |
| `ToggleWifi()` | Change with `wifi` parameter in [mobile:setConnectivity](https://github.com/appium/appium-uiautomator2-driver#mobile-setconnectivity) |
| `SetConnectionType()` | Change connection type with [mobile:setConnectivity](https://github.com/appium/appium-uiautomator2-driver#mobile-setconnectivity) |
| `GetConnectionType()` | Get connection state with [mobile:getConnectivity](https://github.com/appium/appium-uiautomator2-driver#mobile-getconnectivity) |
| `EndTestCoverage()` | Already deprecated. |
| `StartActivityWithIntent()`, `StartActivity()` | Use [mobile:startActivity](https://github.com/appium/appium-uiautomator2-drive#mobile-startactivity) to start an expected activity |
| `strategy` argument in `HideKeyboard()` | It worked only deprecated [old ios driver](https://github.com/appium-boneyard/appium-ios-driver) |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand that part.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a removal which worked only for outdated appium-ios-driver. I'll exclude this from the table.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


## v5

### Appium server compatibility for v5.x

> [!IMPORTANT]
Expand All @@ -44,24 +59,24 @@ App management: Please read [issue #15807](https://github.com/appium/appium/issu
### Migration Guide to W3C actions
```csharp
using OpenQA.Selenium.Interactions;

var touch = new PointerInputDevice(PointerKind.Touch, "finger");
var sequence = new ActionSequence(touch);
var move = touch.CreatePointerMove(elementToTouch, elementToTouch.Location.X, elementToTouch.Location.Y,TimeSpan.FromSeconds(1));
var actionPress = touch.CreatePointerDown(MouseButton.Touch);
var pause = touch.CreatePause(TimeSpan.FromMilliseconds(250));
var actionRelease = touch.CreatePointerUp(MouseButton.Touch);

sequence.AddAction(move);
sequence.AddAction(actionPress);
sequence.AddAction(pause);
sequence.AddAction(actionRelease);

var actions_seq = new List<ActionSequence>
{
sequence
};

_driver.PerformActions(actions_seq);
```

Expand All @@ -83,7 +98,7 @@ Dependencies:
- [System.Drawing.Common](https://www.nuget.org/packages/System.Drawing.Common/)

Note: we will NOT publish a signed version of this assembly since the dependencies we access through NuGet do not have a signed version - thus breaking the chain and causing us headaches. With that said, you are more than welcome to download the code and build a signed version yourself.

## Usage

### basics
Expand All @@ -98,7 +113,7 @@ Note: we will NOT publish a signed version of this assembly since the dependenci
[See samples here](https://github.com/appium/sample-code/tree/master/sample-code/examples/dotnet/AppiumDotNetSample)


## Dev Build+Test
## Dev Build+Test

Xamarin/Mono
- Open with [Xamarin](http://xamarin.com/)
Expand All @@ -117,7 +132,7 @@ Visual Studio

## Nuget Deployment (for maintainers)

### To Setup Nuget
### To Setup Nuget
- Download [Nuget exe](http://nuget.org/nuget.exe).
- Setup the Api Key ([see here](http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package#api-key)).
- `alias NuGet='mono <Nuget Path>/NuGet.exe'`
Expand Down
229 changes: 112 additions & 117 deletions src/Appium.Net/Appium/Android/AndroidCommandExecutionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,151 +22,146 @@ namespace OpenQA.Selenium.Appium.Android
{
public sealed class AndroidCommandExecutionHelper : AppiumCommandExecutionHelper
{
public static void StartActivity(IExecuteMethod executeMethod, string appPackage, string appActivity,
string appWaitPackage = "", string appWaitActivity = "", bool stopApp = true)
public static string GetCurrentActivity(IExecuteMethod executeMethod)
{
Contract.Requires(!string.IsNullOrWhiteSpace(appPackage));
Contract.Requires(!string.IsNullOrWhiteSpace(appActivity));

Dictionary<string, object> parameters = new Dictionary<string, object>()
{
["appPackage"] = appPackage,
["appActivity"] = appActivity,
["appWaitPackage"] = appWaitPackage,
["appWaitActivity"] = appWaitActivity,
["dontStopAppOnReset"] = !stopApp
};

executeMethod.Execute(AppiumDriverCommand.StartActivity, parameters);
return executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getCurrentActivity",
["args"] = Array.Empty<object>()
}).Value.ToString();
}

public static void StartActivityWithIntent(IExecuteMethod executeMethod, string appPackage, string appActivity,
string intentAction, string appWaitPackage = "", string appWaitActivity = "",
string intentCategory = "", string intentFlags = "", string intentOptionalArgs = "", bool stopApp = true)
public static string GetCurrentPackage(IExecuteMethod executeMethod)
{
Contract.Requires(!string.IsNullOrWhiteSpace(appPackage));
Contract.Requires(!string.IsNullOrWhiteSpace(appActivity));
Contract.Requires(!string.IsNullOrWhiteSpace(intentAction));

Dictionary<string, object> parameters = new Dictionary<string, object>()
{
["appPackage"] = appPackage,
["appActivity"] = appActivity,
["appWaitPackage"] = appWaitPackage,
["appWaitActivity"] = appWaitActivity,
["dontStopAppOnReset"] = !stopApp,
["intentAction"] = intentAction,
["intentCategory"] = intentCategory,
["intentFlags"] = intentFlags,
["optionalIntentArguments"] = intentOptionalArgs
};

executeMethod.Execute(AppiumDriverCommand.StartActivity, parameters);
return executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getCurrentPackage",
["args"] = Array.Empty<object>()
}).Value.ToString();
}

public static string GetCurrentActivity(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.GetCurrentActivity).Value as string;

public static string GetCurrentPackage(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.GetCurrentPackage).Value as string;
#region Device Network

public static void SetConection(IExecuteMethod executeMethod, ConnectionType connectionType)
{
Dictionary<string, object> values = new Dictionary<string, object>()
{
["type"] = connectionType
};

Dictionary<string, object> dictionary = new Dictionary<string, object>()
{
["name"] = "network_connection",
["parameters"] = values
};

executeMethod.Execute(AppiumDriverCommand.SetConnectionType, dictionary);
public static void ToggleLocationServices(IExecuteMethod executeMethod) {
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:toggleGps",
["args"] = Array.Empty<object>()
});
}

public static ConnectionType GetConection(IExecuteMethod executeMethod)
{
var commandResponse = executeMethod.Execute(AppiumDriverCommand.GetConnectionType, null);
if (commandResponse.Status == WebDriverResult.Success)
{
return (ConnectionType) (long) commandResponse.Value;
}
else
{
throw new WebDriverException("The request to get the ConnectionType has failed.");
}
public static void GsmCall(IExecuteMethod executeMethod, string number, GsmCallActions gsmCallAction) {
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:gsmCall",
["args"] = new object[] {
new Dictionary<string, object>() {
["phoneNumber"] = number,
["action"] = gsmCallAction.ToString().ToLowerInvariant()
}
}
});
}

#region Device Network

public static void ToggleLocationServices(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.ToggleLocationServices);

public static void ToggleAirplaneMode(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.ToggleAirplaneMode);

public static void ToggleData(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.ToggleData);

public static void ToggleWifi(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.ToggleWiFi);

public static void GsmCall(IExecuteMethod executeMethod, string number, GsmCallActions gsmCallAction) =>
executeMethod.Execute(AppiumDriverCommand.GsmCall,
PrepareArguments(new[] {"phoneNumber", "action"},
new object[] {number, gsmCallAction.ToString().ToLowerInvariant()}));

public static void SendSms(IExecuteMethod executeMethod, string number, string message) =>
executeMethod.Execute(AppiumDriverCommand.SendSms,
PrepareArguments(new[] { "phoneNumber", "message" },
new object[] { number, message }));
public static void SendSms(IExecuteMethod executeMethod, string number, string message) {
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:sendSms",
["args"] = new object[] {
new Dictionary<string, object>() {
["phoneNumber"] = number,
["message"] = message
}
}
});
}

public static void SetGsmStrength(IExecuteMethod executeMethod, GsmSignalStrength gsmSignalStrength) =>
executeMethod.Execute(AppiumDriverCommand.SetGsmSignalStrength,
PrepareArgument("signalStrength", gsmSignalStrength));
public static void SetGsmStrength(IExecuteMethod executeMethod, GsmSignalStrength gsmSignalStrength)
{
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:gsmSignal",
["args"] = new object[] {
new Dictionary<string, object>() {
["strength"] = gsmSignalStrength,
}
}
});
}

public static void SetGsmVoice(IExecuteMethod executeMethod, GsmVoiceState gsmVoiceState) =>
executeMethod.Execute(AppiumDriverCommand.SetGsmVoiceState,
PrepareArgument("state",
gsmVoiceState.ToString()
.ToLowerInvariant()));
public static void SetGsmVoice(IExecuteMethod executeMethod, GsmVoiceState gsmVoiceState)
{
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:gsmVoice",
["args"] = new object[] {
new Dictionary<string, object>()
{
["state"] = gsmVoiceState.ToString().ToLowerInvariant(),
}
}
});
}

#endregion

public static string EndTestCoverage(IExecuteMethod executeMethod, string intent, string path) =>
executeMethod.Execute(AppiumDriverCommand.EndTestCoverage,
new Dictionary<string, object>()
{["intent"] = intent, ["path"] = path}).Value as string;

#region Device Performance

public static object[] GetPerformanceDataTypes(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.GetPerformanceDataTypes).Value as object[];
public static object[] GetPerformanceDataTypes(IExecuteMethod executeMethod) {
return executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getPerformanceDataTypes",
["args"] = Array.Empty<object>()
}).Value as object[];
}

public static object[] GetPerformanceData(IExecuteMethod executeMethod, string packageName,
string dataType) => executeMethod.Execute(AppiumDriverCommand.GetPerformanceData,
PrepareArguments(new[] {"packageName", "dataType"},
new object[] {packageName, dataType})).Value as object[];
public static object[] GetPerformanceData(IExecuteMethod executeMethod, string packageName, string dataType)
{
return executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getPerformanceData",
["args"] = new object[] {
PrepareArguments(
new[] {"packageName", "dataType"},
new object[] {packageName, dataType}
)
}
}).Value as object[];
}

public static object[] GetPerformanceData(IExecuteMethod executeMethod, string packageName,
string dataType, int dataReadTimeout) => executeMethod.Execute(AppiumDriverCommand.GetPerformanceData,
PrepareArguments(new[] {"packageName", "dataType", "dataReadTimeout"},
new object[] {packageName, dataType, dataReadTimeout})).Value as object[];
string dataType, int dataReadTimeout)
{
return executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getPerformanceData",
["args"] = new object[] {
PrepareArguments(
new[] {"packageName", "dataType", "dataReadTimeout"},
new object[] {packageName, dataType, dataReadTimeout}
)
}
}).Value as object[];
}

#endregion

#region Device System

public static void OpenNotifications(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.OpenNotifications);
public static void OpenNotifications(IExecuteMethod executeMethod)
{
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:openNotifications",
["args"] = Array.Empty<object>()
});
}

public static IDictionary<string, object> GetSystemBars(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.SystemBars).Value as IDictionary<string, object>;
public static IDictionary<string, object> GetSystemBars(IExecuteMethod executeMethod)
{
return executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getSystemBars",
["args"] = Array.Empty<object>()
}).Value as IDictionary<string, object>;
}

public static float GetDisplayDensity(IExecuteMethod executeMethod) => Convert.ToSingle(
executeMethod.Execute(AppiumDriverCommand.GetDisplayDensity).Value);
public static float GetDisplayDensity(IExecuteMethod executeMethod)
{
return Convert.ToSingle(
executeMethod.Execute(DriverCommand.ExecuteScript, new Dictionary<string, object> {
["script"] = "mobile:getDisplayDensity",
["args"] = Array.Empty<object>()
}).Value);
}

#endregion

Expand Down
Loading