Skip to content

Commit

Permalink
Feature/keyboard and mouse actions (#12) +semver: feature
Browse files Browse the repository at this point in the history
* Add KeyboardActions for Application and Element to resolve #9

* Add localization for new actions.

* Refactor ApplicationManager and Factories. Add setters to Application and ServiceProvider properties. Rework container registration

* Implemented MouseActions for custom element and for the whole application to resolve #8 . Add Scroll as mouse action to resolve #14

* Add xml documentation to library. Fix documentation for some classes.
  • Loading branch information
mialeska authored Sep 20, 2019
1 parent 9c420da commit 43f86a9
Show file tree
Hide file tree
Showing 34 changed files with 1,554 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Aquality.Selenium.Core.Localization;
using OpenQA.Selenium.Appium.Windows;
using System;
using SeleniumActions = OpenQA.Selenium.Interactions.Actions;

namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Abstract class for any actions against the whole application.
/// </summary>
public abstract class ApplicationActions
{
private readonly LocalizationLogger localizationLogger;
private readonly Func<WindowsDriver<WindowsElement>> windowsDriverSupplier;


/// <summary>
/// Instantiates Aplication actions.
/// </summary>
/// <param name="localizationLogger">Logger for localized values.</param>
/// <param name="windowsDriverSupplier">Method to get current application session.</param>
protected ApplicationActions(LocalizationLogger localizationLogger, Func<WindowsDriver<WindowsElement>> windowsDriverSupplier)
{
this.localizationLogger = localizationLogger;
this.windowsDriverSupplier = windowsDriverSupplier;
}

/// <summary>
/// Performs submitted action against new <see cref="SeleniumActions"/> object.
/// </summary>
/// <param name="action">Action to be performed.</param>
protected virtual void PerformAction(Func<SeleniumActions, SeleniumActions> action)
{
action(new SeleniumActions(windowsDriverSupplier())).Build().Perform();
}

/// <summary>
/// Logs keyboard action in specific format.
/// </summary>
/// <param name="messageKey">Key of the localized message.</param>
/// <param name="args">Arguments for the localized message.</param>
protected virtual void LogAction(string messageKey, params object[] args)
{
localizationLogger.Info(messageKey, args);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Provides methods representing basic keyboard actions.
/// </summary>
public interface IKeyboardActions
{
/// <summary>
/// Presses a key.
/// </summary>
/// <param name="keyToPress">The <see cref="ModifierKey"/> value representing the key to press.</param>
void PressKey(ModifierKey keyToPress);

/// <summary>
/// Releases a key.
/// </summary>
/// <param name="keyToRelease">The <see cref="ModifierKey"/> value representing the key to release.</param>
void ReleaseKey(ModifierKey keyToRelease);

/// <summary>
/// Sends a sequence of keystrokes to the target.
/// </summary>
/// <param name="keySequence">A string representing the keystrokes to send.</param>
void SendKeys(string keySequence);

/// <summary>
/// Sends a sequence of keystrokes to the application, holding a specified key.
/// After the action, holded key is released.
/// </summary>
/// <param name="keySequence">A string representing the keystrokes to send.</param>
/// <param name="keyToHold">The <see cref="ModifierKey"/> value representing the key to hold.</param>
void SendKeysWithKeyHold(string keySequence, ModifierKey keyToHold);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Provides methods representing basic mouse actions.
/// </summary>
public interface IMouseActions
{
/// <summary>
/// Clicks the mouse at the last known mouse coordinates.
/// </summary>
void Click();

/// <summary>
/// Clicks and holds the mouse button at the last known mouse coordinates.
/// </summary>
void ClickAndHold();

/// <summary>
/// Releases the mouse button at the last known mouse coordinates.
/// </summary>
void Release();

/// <summary>
/// Right-clicks the mouse at the last known mouse coordinates.
/// </summary>
void ContextClick();

/// <summary>
/// Double-clicks the mouse at the last known mouse coordinates.
/// </summary>
void DoubleClick();

/// <summary>
/// Moves the mouse to the specified offset of the last known mouse coordinates.
/// </summary>
/// <param name="offsetX">The horizontal offset to which to move the mouse.</param>
/// <param name="offsetY">The vertical offset to which to move the mouse.</param>
void MoveByOffset(int offsetX, int offsetY);

/// <summary>
/// Scrolls the current screen by specified offset.
/// </summary>
/// <param name="offsetX">The horizontal offset relative to the view port.</param>
/// <param name="offsetY">The vertical offset relative to the view port.</param>
void Scroll(int offsetX, int offsetY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Aquality.Selenium.Core.Localization;
using OpenQA.Selenium.Appium.Windows;
using System;

namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Implements Keyboard actions for the whole application.
/// </summary>
public class KeyboardActions : ApplicationActions, IKeyboardActions
{
public KeyboardActions(LocalizationLogger localizationLogger, Func<WindowsDriver<WindowsElement>> windowsDriverSupplier)
: base(localizationLogger, windowsDriverSupplier)
{
}

public void PressKey(ModifierKey keyToPress)
{
LogAction("loc.keyboard.presskey", keyToPress);
PerformAction(actions => actions.KeyDown(keyToPress.GetKeysString()));
}

public void ReleaseKey(ModifierKey keyToRelease)
{
LogAction("loc.keyboard.releasekey", keyToRelease);
PerformAction(actions => actions.KeyUp(keyToRelease.GetKeysString()));
}

public void SendKeys(string keySequence)
{
LogAction("loc.keyboard.sendkeys", keySequence);
PerformAction(actions => actions.SendKeys(keySequence));
}

public void SendKeysWithKeyHold(string keySequence, ModifierKey keyToHold)
{
var keyToHoldString = keyToHold.GetKeysString();
LogAction("loc.keyboard.sendkeys.withkeyhold", keySequence, keyToHold);
PerformAction(actions => actions.KeyDown(keyToHoldString).SendKeys(keySequence).KeyUp(keyToHoldString));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using OpenQA.Selenium;
using System.Linq;
using System.Reflection;

namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Represents modifier keys which could be used in <see cref="IKeyboardActions"/>.
/// Directly related to <see cref="Keys"/>
/// </summary>
public enum ModifierKey
{
/// <summary>
/// Represents the Alt key.
/// </summary>
Alt,
/// <summary>
/// Represents the function key COMMAND.
/// </summary>
Command,
/// <summary>
/// Represents the Control key.
/// </summary>
Control,
/// <summary>
/// Represents the Left Alt key.
/// </summary>
LeftAlt,
/// <summary>
/// Represents the Left Control key.
/// </summary>
LeftControl,
/// <summary>
/// Represents the Left Shift key.
/// </summary>
LeftShift,
/// <summary>
/// Represents the function key META.
/// </summary>
Meta,
/// <summary>
/// Represents the Shift key.
/// </summary>
Shift
}

internal static class ModifierKeyExtensions
{
public static string GetKeysString(this ModifierKey modifierKey)
{
return typeof(Keys)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault(field => field.Name == modifierKey.ToString())?.GetValue(null).ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Aquality.Selenium.Core.Localization;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;
using System;

namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Implements Mouse actions for the whole application.
/// </summary>
public class MouseActions : ApplicationActions, IMouseActions
{
private readonly Func<RemoteTouchScreen> remoteTouchScreenSupplier;

public MouseActions(LocalizationLogger localizationLogger, Func<WindowsDriver<WindowsElement>> windowsDriverSupplier)
: base(localizationLogger, windowsDriverSupplier)
{
remoteTouchScreenSupplier = () => new RemoteTouchScreen(windowsDriverSupplier());
}

public void Click()
{
LogAction("loc.mouse.click");
PerformAction(actions => actions.Click());
}

public void ClickAndHold()
{
LogAction("loc.mouse.clickandhold");
PerformAction(actions => actions.ClickAndHold());
}

public void Release()
{
LogAction("loc.mouse.release");
PerformAction(actions => actions.Release());
}

public void ContextClick()
{
LogAction("loc.mouse.contextclick");
PerformAction(actions => actions.ContextClick());
}

public void DoubleClick()
{
LogAction("loc.mouse.doubleclick");
PerformAction(actions => actions.DoubleClick());
}

public void MoveByOffset(int offsetX, int offsetY)
{
LogAction("loc.mouse.movebyoffset", offsetX, offsetY);
PerformAction(actions => actions.MoveByOffset(offsetX, offsetY));
}

public void Scroll(int offsetX, int offsetY)
{
LogAction("loc.mouse.scrollbyoffset", offsetX, offsetY);
remoteTouchScreenSupplier().Scroll(offsetX, offsetY);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Aquality.Selenium.Core.Applications;
using Aquality.Selenium.Core.Configurations;
using Aquality.Selenium.Core.Localization;
using Aquality.WinAppDriver.Actions;
using Microsoft.Extensions.DependencyInjection;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;

Expand All @@ -13,19 +15,21 @@ namespace Aquality.WinAppDriver.Applications
public class Application : IApplication
{
private TimeSpan implicitWait;

/// <summary>
/// Instantiate application.
/// </summary>
/// <param name="windowsDriver">Instance of WinAppDriver</param>
/// <param name="timeoutConfiguration">Instance of <see cref="ITimeoutConfiguration"/></param>
/// <param name="logger">Instance of <see cref="LocalizationLogger"/></param>
public Application(WindowsDriver<WindowsElement> windowsDriver, ITimeoutConfiguration timeoutConfiguration, LocalizationLogger logger)
/// <param name="serviceProvider">Service provider to resolve all dependencies from DI container</param>
public Application(WindowsDriver<WindowsElement> windowsDriver, IServiceProvider serviceProvider)
{
Logger = logger;
WindowsDriver = windowsDriver;
Logger = serviceProvider.GetRequiredService<LocalizationLogger>();
KeyboardActions = serviceProvider.GetRequiredService<IKeyboardActions>();
MouseActions = serviceProvider.GetRequiredService<IMouseActions>();
var timeoutConfiguration = serviceProvider.GetRequiredService<ITimeoutConfiguration>();
WindowsDriver.Manage().Timeouts().ImplicitWait = timeoutConfiguration.Implicit;
logger.Info("loc.application.ready");
Logger.Info("loc.application.ready");
}

private LocalizationLogger Logger { get; }
Expand All @@ -37,6 +41,10 @@ public Application(WindowsDriver<WindowsElement> windowsDriver, ITimeoutConfigur
/// </summary>
public WindowsDriver<WindowsElement> WindowsDriver { get; }

public IKeyboardActions KeyboardActions { get; }

public IMouseActions MouseActions { get; }

/// <summary>
/// Sets WinAppDriver ImplicitWait timeout.
/// Default value: <see cref="ITimeoutConfiguration.Implicit"/>.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
using Aquality.Selenium.Core.Localization;
using OpenQA.Selenium.Appium;
using Aquality.Selenium.Core.Configurations;
using Aquality.Selenium.Core.Localization;
using Aquality.WinAppDriver.Configurations;
using Microsoft.Extensions.DependencyInjection;
using OpenQA.Selenium.Appium.Windows;
using System;

namespace Aquality.WinAppDriver.Applications
{
public abstract class ApplicationFactory : IApplicationFactory
{
private readonly LocalizationLogger localizationLogger;
private readonly IDriverSettings driverSettings;
private readonly ITimeoutConfiguration timeoutConfiguration;

protected ApplicationFactory(LocalizationLogger localizationLogger)
protected LocalizationLogger LocalizationLogger { get; }
protected IServiceProvider ServiceProvider { get; }

protected ApplicationFactory(IServiceProvider serviceProvider)
{
this.localizationLogger = localizationLogger;
LocalizationLogger = serviceProvider.GetRequiredService<LocalizationLogger>();
driverSettings = serviceProvider.GetRequiredService<IDriverSettings>();
timeoutConfiguration = serviceProvider.GetRequiredService<ITimeoutConfiguration>();
ServiceProvider = serviceProvider;
}

public abstract Application Application { get; }

protected WindowsDriver<WindowsElement> GetDriver(Uri driverServerUri, AppiumOptions options, TimeSpan commandTimeout)
protected WindowsDriver<WindowsElement> GetDriver(Uri driverServerUri)
{
var options = driverSettings.AppiumOptions;
options.ToDictionary().TryGetValue("app", out var appPath);
localizationLogger.Info("loc.application.start", appPath);
return new WindowsDriver<WindowsElement>(driverServerUri, options, commandTimeout);
LocalizationLogger.Info("loc.application.start", appPath);
return new WindowsDriver<WindowsElement>(driverServerUri, options, timeoutConfiguration.Command);
}
}
}
Loading

0 comments on commit 43f86a9

Please sign in to comment.