Skip to content
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

[WIP] Add gesture support #9

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
217 changes: 217 additions & 0 deletions src/Quamotion.Malaga/TouchAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Text;

namespace Quamotion.Malaga
{ /// <summary>
/// Represents a touch action which can be executed using the WebDriverAgent.
/// </summary>
public class TouchAction
{
/// <summary>
/// Gets the operation being executed.
/// </summary>
[JsonConverter(typeof(StringEnumConverter), /* camelCaseText */ true)]
[JsonProperty("action")]
public TouchOperation Action { get; private set; }

/// <summary>
/// Gets a dictionary which contains the options for this operation.
/// </summary>
[JsonProperty("options")]
public Dictionary<string, object> Options { get; private set; } = new Dictionary<string, object>();

/// <summary>
/// Performs a tap (finger down and finger up) operation.
/// </summary>
/// <param name="x">
/// The x-coordinate of the position at which to tap.
/// </param>
/// <param name="y">
/// The y-coordinate of the position at which to tap.
/// </param>
/// <param name="count">
/// The number of taps to perform.
/// </param>
/// <returns>
/// A <see cref="TouchAction"/> which represents the tap operation.
/// </returns>
public static TouchAction Tap(double x, double y, int count = 1)
{
return new TouchAction()
{
Action = TouchOperation.Tap,
Options = new Dictionary<string, object>()
{
{ nameof(x), x },
{ nameof(y), y },
{ nameof(count), count }
}
};
}

/// <summary>
/// Performs a long press operation.
/// </summary>
/// <param name="x">
/// The x-coordinate of the position at which to tap.
/// </param>
/// <param name="y">
/// The y-coordinate of the position at which to tap.
/// </param>
/// <param name="duration">
/// The duration, in milliseconds, of the tap gesture.
/// </param>
/// <param name="pressure">
/// The amount of pressure to apply.
/// </param>
/// <returns>
/// A <see cref="TouchAction"/> which represents the long press operation.
/// </returns>
public static TouchAction LongPress(double x, double y, double duration = 500, double pressure = 0)
{
return new TouchAction()
{
Action = TouchOperation.LongPress,
Options = new Dictionary<string, object>()
{
{ nameof(x), x },
{ nameof(y), y },
{ nameof(duration), duration },
{ nameof(pressure), pressure }
}
};
}

/// <summary>
/// Performs a finger down operation.
/// </summary>
/// <param name="x">
/// The x-coordinate of the position at which to perform the finger down.
/// </param>
/// <param name="y">
/// The y-coordinate of the position at which to perform the finger down.
/// </param>
/// <param name="pressure">
/// The amount of pressure to apply.
/// </param>
/// <returns>
/// A <see cref="TouchAction"/> which represents the finger down operation.
/// </returns>
public static TouchAction Press(double x, double y, double pressure = 0)
{
return new TouchAction()
{
Action = TouchOperation.Press,
Options = new Dictionary<string, object>()
{
{ nameof(x), x },
{ nameof(y), y },
{ nameof(pressure), pressure }
}
};
}

/// <summary>
/// Performs a finger up operation.
/// </summary>
/// <returns>
/// A <see cref="TouchAction"/> which represents the finger up operation.
/// </returns>
public static TouchAction Release()
{
return new TouchAction()
{
Action = TouchOperation.Release,
Options = new Dictionary<string, object>()
{
}
};
}

/// <summary>
/// Moves the finger to a new position.
/// </summary>
/// <param name="x">
/// The x-coordiante of the position to which to move the finger.
/// </param>
/// <param name="y">
/// The y-coordinate of the position to which to move the finger.
/// </param>
/// <returns>
/// A <see cref="TouchAction"/> which represents the move operation.
/// </returns>
public static TouchAction MoveTo(double x, double y)
{
return new TouchAction()
{
Action = TouchOperation.MoveTo,
Options = new Dictionary<string, object>()
{
{ nameof(x), x },
{ nameof(y), y },
}
};
}

/// <summary>
/// Waits for a specific amount of time.
/// </summary>
/// <param name="timeSpan">
/// The amount of time to wait.
/// </param>
/// <returns>
/// A <see cref="TouchOperation"/> which represents the wait operation.
/// </returns>
public static TouchAction Wait(TimeSpan timeSpan)
{
return Wait(timeSpan.TotalMilliseconds);
}

/// <summary>
/// Waits for a specific amount of time.
/// </summary>
/// <param name="ms">
/// The amount of time to wait, in milliseconds.
/// </param>
/// <returns>
/// A <see cref="TouchOperation"/> which represents the wait operation.
/// </returns>
public static TouchAction Wait(double ms)
{
return new TouchAction()
{
Action = TouchOperation.Wait,
Options = new Dictionary<string, object>()
{
{ nameof(ms), ms },
}
};
}

/// <summary>
/// Cancels the current operation.
/// </summary>
/// <returns>
/// A <see cref="TouchAction"/> which represents the cancel operation.
/// </returns>
public static TouchAction Cancel()
{
return new TouchAction()
{
Action = TouchOperation.Cancel,
Options = new Dictionary<string, object>()
{
}
};
}

/// <inheritdoc/>
public override string ToString()
{
return this.Action.ToString();
}
}
}
47 changes: 47 additions & 0 deletions src/Quamotion.Malaga/TouchOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Quamotion.Malaga
{
/// <summary>
/// Represents a touch operation.
/// </summary>
public enum TouchOperation
{
/// <summary>
/// Performs a single tap.
/// </summary>
Tap,

/// <summary>
/// Performs a finger down and wait operation.
/// </summary>
LongPress,

/// <summary>
/// Performs a finger down operation.
/// </summary>
Press,

/// <summary>
/// Performs a finger up operation.
/// </summary>
Release,

/// <summary>
/// Performs a finger move operation.
/// </summary>
MoveTo,

/// <summary>
/// Performs a wait operation.
/// </summary>
Wait,

/// <summary>
/// Cancels the current operation.
/// </summary>
Cancel
}
}
4 changes: 4 additions & 0 deletions src/Quamotion.Malaga/WdaCommandExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public WdaCommandExecutor(Uri addressOfRemoteServer, string sessionId, TimeSpan

this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.Type, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/keyboard/type"));
this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.PressDeviceButton, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/pressDeviceButton"));

this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.TouchAndHold, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/touchAndHold"));
this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.Drag, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/dragfromtoforduration"));
this.CommandInfoRepository.TryAddCommand(WdaDriverCommand.PerformTouch, new CommandInfo(CommandInfo.PostCommand, "/session/{sessionId}/wda/touch/perform"));
}

public override Response Execute(Command commandToExecute)
Expand Down
98 changes: 98 additions & 0 deletions src/Quamotion.Malaga/WdaDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;

namespace Quamotion.Malaga
{
Expand Down Expand Up @@ -110,6 +111,103 @@ public virtual ScreenOrientation Rotation
}
}

/// <summary>
/// Sends a touch and hold event to the device.
/// </summary>
/// <param name="location">
/// The position at which to touch.
/// </param>
/// <param name="duration">
/// The duration of the touch event, in seconds.
/// </param>
public virtual void TouchAndHold(PointF location, TimeSpan duration)
{
// TODO: find a way to handle the timeout
// var timeout = Math.Max(Timeouts.TestServerRequestTimeout.TotalSeconds, 2 * duration.TotalSeconds);
this.Execute(
WdaDriverCommand.TouchAndHold,
new Dictionary<string, object>()
{
{ "x", location.X },
{ "y", location.Y },
{ "duration", duration.TotalSeconds }
});
}

/// <summary>
/// Sends a drag gesture to the device.
/// </summary>
/// <param name="from">
/// The location at which the drag gesture begins.
/// </param>
/// <param name="to">
/// The location at which the drag gesture ends.
/// </param>
/// <param name="duration">
/// The duration of the drag gesture.
/// </param>
public virtual void Drag(PointF from, PointF to, TimeSpan duration)
{
// Drag on iOS is slow, _very_ slow.
// Increase the timeout to make sure we don't time out (which would be a TaskCancelledException)
// TODO: find a way to work with the timeout
// var timeout = Math.Max(Timeouts.TestServerRequestTimeout.TotalSeconds, 15 + (5 * duration.TotalSeconds));

this.Execute(
WdaDriverCommand.Drag,
new Dictionary<string, object>()
{

{ "fromX", from.X },
{ "fromY", from.Y },
{ "toX", to.X },
{ "toY", to.Y },
{ "duration", duration.TotalSeconds }
});
}

/// <summary>
/// Performs a sequence of touch actions.
/// </summary>
/// <param name="touches">
/// The touch actions to execute.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation.
/// </returns>
public virtual void PerformTouch(Collection<TouchAction> touches)
{
double duration = 0;

foreach (var touch in touches)
{
if (touch.Action == TouchOperation.Wait)
{
duration += (double)touch.Options["ms"];
}
else if (touch.Action == TouchOperation.LongPress)
{
duration += (double)touch.Options["duration"];
}
}

// TODO: figure out a way to pass the timeout
// TimeSpan.FromMilliseconds(Timeouts.TestServerRequestTimeout.TotalMilliseconds + (2 * duration)),

var json = JsonConvert.SerializeObject(
new
{
actions = touches
});

this.Execute(
WdaDriverCommand.PerformTouch
json);
}

/// <summary>
/// Gets the text of the current alert.
/// </summary>
Expand Down
Loading