Skip to content

Commit

Permalink
Support clipboard endpoint (#278)
Browse files Browse the repository at this point in the history
* Add clipboard endpoints for AndroidDriver & IOSDriver. resolves #252 and #234
  • Loading branch information
laolubenson committed Oct 29, 2018
1 parent 509247a commit 3284bd6
Show file tree
Hide file tree
Showing 12 changed files with 634 additions and 11 deletions.
56 changes: 55 additions & 1 deletion appium-dotnet-driver/Appium/Android/AndroidDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
using System.Text;
using System.IO;
using System.Collections.Generic;
using System.Drawing;
using OpenQA.Selenium.Appium.Android.Enums;

namespace OpenQA.Selenium.Appium.Android
{
public class AndroidDriver<W> : AppiumDriver<W>, IFindByAndroidUIAutomator<W>, IStartsActivity,
IHasNetworkConnection,
IHasNetworkConnection, IHasClipboard,
ISendsKeyEvents,
IPushesFiles, IHasSettings where W : IWebElement
{
Expand Down Expand Up @@ -255,5 +256,58 @@ public Dictionary<string, object> Settings
}
}
}

/// <summary>
/// Sets the content to the clipboard
/// </summary>
/// <param name="contentType"></param>
/// <param name="base64Content"></param>
public void SetClipboard(ClipboardContentType contentType, string base64Content) => AppiumCommandExecutionHelper.SetClipboard(this, contentType, base64Content);

/// <summary>
/// Get the content of the clipboard.
/// </summary>
/// <param name="contentType"></param>
/// <remarks>Android supports plaintext only</remarks>
/// <returns>The content of the clipboard as base64-encoded string or an empty string if the clipboard is empty</returns>
public string GetClipboard(ClipboardContentType contentType) => AppiumCommandExecutionHelper.GetClipboard(this, contentType);

/// <summary>
/// Sets text to the clipboard
/// </summary>
/// <param name="textContent"></param>
/// <param name="label">For Android only - A user visible label for the clipboard content.</param>
public void SetClipboardText(string textContent, string label) => AppiumCommandExecutionHelper.SetClipboardText(this, textContent, label);

/// <summary>
/// Get the plaintext content of the clipboard.
/// </summary>
/// <remarks>Android supports plaintext only</remarks>
/// <returns>The string content of the clipboard or an empty string if the clipboard is empty</returns>
public string GetClipboardText() => AppiumCommandExecutionHelper.GetClipboardText(this);

/// <summary>
/// Sets the url string to the clipboard
/// </summary>
/// <param name="url"></param>
public void SetClipboardUrl(string url) => throw new NotImplementedException();

/// <summary>
/// Gets the url string from the clipboard
/// </summary>
/// <returns>The url string content of the clipboard or an empty string if the clipboard is empty</returns>
public string GetClipboardUrl() => throw new NotImplementedException();

/// <summary>
/// Sets the image to the clipboard
/// </summary>
/// <param name="image"></param>
public void SetClipboardImage(Image image) => throw new NotImplementedException();

/// <summary>
/// Gets the image from the clipboard
/// </summary>
/// <returns>The image content of the clipboard as base64-encoded string or null if there is no image on the clipboard</returns>
public Image GetClipboardImage() => throw new NotImplementedException();
}
}
11 changes: 9 additions & 2 deletions appium-dotnet-driver/Appium/AppiumCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ public class AppiumCommand

#endregion Context Commands

#region Appium Commands
#region Driver Commands

#region Clipboard

new AppiumCommand(CommandInfo.PostCommand, AppiumDriverCommand.GetClipboard, "/session/{sessionId}/appium/device/get_clipboard"),
new AppiumCommand(CommandInfo.PostCommand, AppiumDriverCommand.SetClipboard, "/session/{sessionId}/appium/device/set_clipboard"),

#endregion

new AppiumCommand(CommandInfo.PostCommand, AppiumDriverCommand.ShakeDevice,
"/session/{sessionId}/appium/device/shake"),
new AppiumCommand(CommandInfo.PostCommand, AppiumDriverCommand.LockDevice,
Expand Down Expand Up @@ -93,7 +100,7 @@ public class AppiumCommand
new AppiumCommand(CommandInfo.PostCommand, AppiumDriverCommand.TouchID,
"/session/{sessionId}/appium/simulator/touch_id"),

#endregion Appium Commands
#endregion Driver Commands

#region Touch Commands

Expand Down
102 changes: 99 additions & 3 deletions appium-dotnet-driver/Appium/AppiumCommandExecutionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@
//See the License for the specific language governing permissions and
//limitations under the License.

using System;
using OpenQA.Selenium.Appium.Interfaces;
using System.Collections.Generic;
using System.Text;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.Enums;

namespace OpenQA.Selenium.Appium
{
public class AppiumCommandExecutionHelper
{
#region Device Commands

public static void PressKeyCode(IExecuteMethod executeMethod, int keyCode, int metastate = -1)
{
var parameters = new Dictionary<string, object>()
{["keycode"] = keyCode};
{ ["keycode"] = keyCode };
if (metastate > 0)
{
parameters.Add("metastate", metastate);
Expand All @@ -33,7 +39,7 @@ public static void PressKeyCode(IExecuteMethod executeMethod, int keyCode, int m
public static void LongPressKeyCode(IExecuteMethod executeMethod, int keyCode, int metastate = -1)
{
var parameters = new Dictionary<string, object>()
{["keycode"] = keyCode};
{ ["keycode"] = keyCode };
if (metastate > 0)
{
parameters.Add("metastate", metastate);
Expand All @@ -58,6 +64,96 @@ public static void HideKeyboard(IExecuteMethod executeMethod, string strategy =
public static void Lock(IExecuteMethod executeMethod, int seconds) =>
executeMethod.Execute(AppiumDriverCommand.LockDevice,
new Dictionary<string, object>()
{["seconds"] = seconds});
{ ["seconds"] = seconds });

public static void SetClipboard(IExecuteMethod executeMethod, ClipboardContentType clipboardContentType, string base64Content)
{
switch (clipboardContentType)
{
case ClipboardContentType.Image:
case ClipboardContentType.Url:
if (executeMethod.GetType().GetGenericTypeDefinition() == typeof(AndroidDriver<>))
{ throw new NotImplementedException($"Android only supports contentType: {nameof(ClipboardContentType.PlainText)}"); }

executeMethod.Execute(AppiumDriverCommand.SetClipboard,
PrepareArguments(new[] { "content", "contentType", "label" },
new object[] { base64Content, clipboardContentType.ToString().ToLower(), null }));
break;
default:
executeMethod.Execute(AppiumDriverCommand.SetClipboard,
PrepareArguments(new[] { "content", "contentType", "label" },
new object[] { base64Content, clipboardContentType.ToString().ToLower(), null }));
break;
}
}

public static string GetClipboard(IExecuteMethod executeMethod, ClipboardContentType clipboardContentType)
{
switch (clipboardContentType)
{
case ClipboardContentType.Image:
case ClipboardContentType.Url:
if (executeMethod.GetType().GetGenericTypeDefinition() == typeof(AndroidDriver<>))
{ throw new NotImplementedException($"Android only supports contentType: {nameof(ClipboardContentType.PlainText)}"); }
return (string)executeMethod.Execute(AppiumDriverCommand.GetClipboard,
PrepareArgument("contentType", clipboardContentType.ToString().ToLower())).Value;
case ClipboardContentType.PlainText:
return (string)executeMethod.Execute(AppiumDriverCommand.GetClipboard,
PrepareArgument("contentType", clipboardContentType.ToString().ToLower())).Value;
default:
return (string)executeMethod.Execute(AppiumDriverCommand.GetClipboard,
PrepareArgument("contentType", clipboardContentType.ToString().ToLower())).Value;
}
}

public static string SetClipboardText(IExecuteMethod executeMethod, string textContent, string label)
{
if (textContent == null) { throw new ArgumentException($"{nameof(textContent)} cannot be null"); }
var encodedStringContentBytes = Encoding.UTF8.GetBytes(textContent);

return (string)executeMethod.Execute(AppiumDriverCommand.SetClipboard,
PrepareArguments(new[] { "content", "contentType", "label" },
new object[] { Convert.ToBase64String(encodedStringContentBytes), ClipboardContentType.PlainText.ToString().ToLower(), label })).Value;
}

public static string GetClipboardText(IExecuteMethod executeMethod)
{
var encodedContentBytes = Convert.FromBase64String(GetClipboard(executeMethod, ClipboardContentType.PlainText));
return Encoding.UTF8.GetString(encodedContentBytes);
}

#endregion Device Commands

/// <summary>
/// Prepares single command argument
/// </summary>
/// <param name="name">The parameter name</param>
/// <param name="value">The parameter value</param>
/// <returns></returns>
internal static Dictionary<string, object> PrepareArgument(string name, object value)
{
return new Dictionary<string, object> { { name, value } };
}

/// <summary>
/// Prepares a collection of command arguments
/// </summary>
/// <param name="names">The array of parameter names</param>
/// <param name="values">The array of parameter values</param>
/// <returns></returns>
internal static Dictionary<string, object> PrepareArguments(string[] names, object[] values)
{
var parameterBuilder = new Dictionary<string, object>();
if (names.Length != values.Length) return parameterBuilder;
for (var i = 0; i < names.Length; i++)
{
if (names[i] != string.Empty && values[i] != null)
{
parameterBuilder.Add(names[i], values[i]);
}
}

return parameterBuilder;
}
}
}
4 changes: 4 additions & 0 deletions appium-dotnet-driver/Appium/AppiumDriverCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ public class AppiumDriverCommand
/// </summary>
public const string GetLocation = "getLocation";

public const string GetClipboard = "getClipboard";

public const string SetClipboard = "setClipboard";

#endregion Appium Specific extensions to JSONWP Commands

#region TouchActions
Expand Down
15 changes: 15 additions & 0 deletions appium-dotnet-driver/Appium/Enums/ClipboardContentType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenQA.Selenium.Appium.Enums
{
public enum ClipboardContentType
{
PlainText,
Image,
Url
}
}
68 changes: 68 additions & 0 deletions appium-dotnet-driver/Appium/Interfaces/IHasClipboard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Drawing;
using OpenQA.Selenium.Appium.Enums;

namespace OpenQA.Selenium.Appium.Interfaces
{
public interface IHasClipboard
{
#region base methods

/// <summary>
/// Sets the content to the clipboard
/// </summary>
/// <param name="contentType"></param>
/// <param name="base64Content"></param>
void SetClipboard(ClipboardContentType contentType, string base64Content);

/// <summary>
/// Get the content of the clipboard.
/// </summary>
/// <param name="contentType"></param>
/// <remarks>Android supports plaintext only</remarks>
/// <returns>The content of the clipboard as base64-encoded string or an empty string if the clipboard is empty</returns>
string GetClipboard(ClipboardContentType contentType);

#endregion

/// <summary>
/// Sets text to the clipboard
/// </summary>
/// <param name="textContent"></param>
/// <param name="label">For Android only - A user visible label for the clipboard content.</param>
void SetClipboardText(string textContent, string label);

/// <summary>
/// Get the plaintext content of the clipboard.
/// </summary>
/// <remarks>Android supports plaintext only</remarks>
/// <returns>The string content of the clipboard or an empty string if the clipboard is empty</returns>
string GetClipboardText();

/// <summary>
/// Sets the url string to the clipboard
/// </summary>
/// <param name="url"></param>
void SetClipboardUrl(string url);

/// <summary>
/// Gets the url string from the clipboard
/// </summary>
/// <returns>The url string content of the clipboard or an empty string if the clipboard is empty</returns>
string GetClipboardUrl();

/// <summary>
/// Sets the image to the clipboard
/// </summary>
/// <param name="image"></param>
void SetClipboardImage(Image image);

/// <summary>
/// Gets the image from the clipboard
/// </summary>
/// <returns>The image content of the clipboard as base64-encoded string or an empty string the clipboard is empty</returns>
Image GetClipboardImage();



}
}
54 changes: 54 additions & 0 deletions appium-dotnet-driver/Appium/iOS/IOSCommandExecutionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@
//See the License for the specific language governing permissions and
//limitations under the License.

using System;
using OpenQA.Selenium.Appium.Interfaces;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Text;
using OpenQA.Selenium.Appium.Enums;

namespace OpenQA.Selenium.Appium.iOS
{
Expand All @@ -35,5 +42,52 @@ public static void Unlock(IExecuteMethod executeMethod) =>
public static void Lock(IExecuteMethod executeMethod) =>
executeMethod.Execute(AppiumDriverCommand.LockDevice);

public static void SetClipboardUrl(IExecuteMethod executeMethod, string url)
{
var urlEncoded = WebUtility.UrlEncode(url);
var base64UrlBytes = Encoding.UTF8.GetBytes(urlEncoded);
AppiumCommandExecutionHelper.SetClipboard(executeMethod, ClipboardContentType.Url, Convert.ToBase64String(base64UrlBytes));
}

public static string GetClipboardUrl(IExecuteMethod executeMethod)
{
var content = AppiumCommandExecutionHelper.GetClipboard(executeMethod, ClipboardContentType.Url);
var urlEncodedBytes = Convert.FromBase64String(content);
var urlDecodedBytes = Encoding.UTF8.GetString(urlEncodedBytes);

return WebUtility.UrlDecode(urlDecodedBytes);
}

public static void SetClipboardImage(IExecuteMethod executeMethod, Image image)
{
byte[] imageBytes;
using (var memoryStream = new MemoryStream())
{
image.Save(memoryStream, ImageFormat.Png);
imageBytes = memoryStream.ToArray();
}

AppiumCommandExecutionHelper.SetClipboard(executeMethod,
ClipboardContentType.Image,
Convert.ToBase64String(imageBytes));
}

public static void SetClipboardImage(IExecuteMethod executeMethod, string base64EncodeImage)
{
AppiumCommandExecutionHelper.SetClipboard(executeMethod,ClipboardContentType.Image, base64EncodeImage);
}

public static Image GetClipboardImage(IExecuteMethod executeMethod)
{
var imageBytes = Convert.FromBase64String(
AppiumCommandExecutionHelper.GetClipboard(executeMethod, ClipboardContentType.Image));

if (imageBytes.Length > 0)
{
Image.FromStream(new MemoryStream(imageBytes));
}

return null;
}
}
}
Loading

1 comment on commit 3284bd6

@PraveenNalla23
Copy link

Choose a reason for hiding this comment

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

Hi,
May I know in which version this fix will be released. I am not seeing these changes in 4.0.0.4 Beta version.

Thanks,
Praveen

Please sign in to comment.