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

Feature/keyboard and mouse actions #12

Merged
merged 13 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace Aquality.WinAppDriver.Actions
{
/// <summary>
/// Provides methods representing basic keyboard actions.
/// </summary>
public interface IKeyboardActions
{
/// <summary>
/// Presses a key.
/// </summary>
/// <param name="keyToPress">The key value representing the key to press.</param>
/// <remarks>The key value must be one of the values from the <see cref="OpenQA.Selenium.Keys"/> class.</remarks>
/// <exception cref="System.ArgumentException">If the key sent is not is not one of:
/// <see cref="OpenQA.Selenium.Keys.Shift"/>,
/// <see cref="OpenQA.Selenium.Keys.Control"/>,
/// <see cref="OpenQA.Selenium.Keys.Alt"/>,
/// <see cref="OpenQA.Selenium.Keys.Meta"/>,
/// <see cref="OpenQA.Selenium.Keys.Command"/>,
/// <see cref="OpenQA.Selenium.Keys.LeftAlt"/>,
/// <see cref="OpenQA.Selenium.Keys.LeftShift"/>,
/// <see cref="OpenQA.Selenium.Keys.Shift"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you really need this list of Keys in summary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes I do. Selenium has it. We either need to follow it's notation, or define a custom Enum with mapping to this values, and accept only them in our methods. I like the first variant more, but we can discuss it

/// </exception>
void PressKey(string keyToPress);

/// <summary>
/// Releases a key.
/// </summary>
/// <param name="keyToRelease">The key value representing the key to release.</param>
/// <remarks>The key value must be one of the values from the <see cref="OpenQA.Selenium.Keys"/> class.</remarks>
/// <exception cref="System.ArgumentException">If the key sent is not is not one of:
/// <see cref="OpenQA.Selenium.Keys.Shift"/>,
/// <see cref="OpenQA.Selenium.Keys.Control"/>,
/// <see cref="OpenQA.Selenium.Keys.Alt"/>,
/// <see cref="OpenQA.Selenium.Keys.Meta"/>,
/// <see cref="OpenQA.Selenium.Keys.Command"/>,
/// <see cref="OpenQA.Selenium.Keys.LeftAlt"/>,
/// <see cref="OpenQA.Selenium.Keys.LeftShift"/>,
/// <see cref="OpenQA.Selenium.Keys.Shift"/>
/// </exception>
void ReleaseKey(string 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 target, 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 key value representing the key to hold.
/// <remarks>The key value must be one of the values from the <see cref="OpenQA.Selenium.Keys"/> class.</remarks></param>
/// <exception cref="System.ArgumentException">If the key sent is not is not one of:
/// <see cref="OpenQA.Selenium.Keys.Shift"/>,
/// <see cref="OpenQA.Selenium.Keys.Control"/>,
/// <see cref="OpenQA.Selenium.Keys.Alt"/>,
/// <see cref="OpenQA.Selenium.Keys.Meta"/>,
/// <see cref="OpenQA.Selenium.Keys.Command"/>,
/// <see cref="OpenQA.Selenium.Keys.LeftAlt"/>,
/// <see cref="OpenQA.Selenium.Keys.LeftShift"/>,
/// <see cref="OpenQA.Selenium.Keys.Shift"/>
/// </exception>
void SendKeysWithKeyHold(string keySequence, string keyToHold);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Aquality.Selenium.Core.Localization;
using Aquality.WinAppDriver.Extensions;
using OpenQA.Selenium.Appium.Windows;
using System;
using SeleniumActions = OpenQA.Selenium.Interactions.Actions;

namespace Aquality.WinAppDriver.Actions
{
public class KeyboardActions : IKeyboardActions
{
private readonly LocalizationLogger localizationLogger;
private readonly WindowsDriver<WindowsElement> windowsDriver;

public KeyboardActions(LocalizationLogger localizationLogger, WindowsDriver<WindowsElement> windowsDriver)
{
this.localizationLogger = localizationLogger;
this.windowsDriver = windowsDriver;
}

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

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

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

public void SendKeysWithKeyHold(string keySequence, string keyToHold)
{
LogAction("loc.keyboard.sendkeys.withkeyhold", keySequence, keyToHold.GetLoggableValueForKeyboardKey());
PerformAction(actions => actions.KeyDown(keyToHold).SendKeys(keySequence).KeyUp(keyToHold));
}

/// <summary>
/// Performs submited action against new <see cref="SeleniumActions"/> object.
mialeska marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="action">Action to be performed.</param>
protected virtual void PerformAction(Func<SeleniumActions, SeleniumActions> action)
{
action(new SeleniumActions(windowsDriver)).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>
mialeska marked this conversation as resolved.
Show resolved Hide resolved
protected virtual void LogAction(string messageKey, params object[] args)
{
localizationLogger.Info(messageKey, args);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Aquality.Selenium.Core.Applications;
using Aquality.Selenium.Core.Configurations;
using Aquality.Selenium.Core.Localization;
using Aquality.WinAppDriver.Actions;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;

Expand All @@ -20,11 +21,12 @@ public class Application : IApplication
/// <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)
public Application(WindowsDriver<WindowsElement> windowsDriver, ITimeoutConfiguration timeoutConfiguration, LocalizationLogger logger, IKeyboardActions keyboardActions = null)
{
Logger = logger;
WindowsDriver = windowsDriver;
WindowsDriver.Manage().Timeouts().ImplicitWait = timeoutConfiguration.Implicit;
KeyboardActions = keyboardActions ?? new KeyboardActions(logger, windowsDriver);
logger.Info("loc.application.ready");
}

Expand All @@ -37,6 +39,8 @@ public Application(WindowsDriver<WindowsElement> windowsDriver, ITimeoutConfigur
/// </summary>
public WindowsDriver<WindowsElement> WindowsDriver { get; }

public IKeyboardActions KeyboardActions { get; }

/// <summary>
/// Sets WinAppDriver ImplicitWait timeout.
/// Default value: <see cref="ITimeoutConfiguration.Implicit"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using OpenQA.Selenium.Appium.Service;
using Aquality.Selenium.Core.Logging;
using System.Reflection;
using Aquality.WinAppDriver.Actions;
mialeska marked this conversation as resolved.
Show resolved Hide resolved

namespace Aquality.WinAppDriver.Applications
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Aquality.Selenium.Core.Applications;
using Aquality.Selenium.Core.Localization;
using Aquality.Selenium.Core.Utilities;
using Aquality.WinAppDriver.Actions;
using Aquality.WinAppDriver.Elements.Interfaces;
using Aquality.WinAppDriver.Extensions;
using OpenQA.Selenium;
using System;
using SeleniumActions = OpenQA.Selenium.Interactions.Actions;

namespace Aquality.WinAppDriver.Elements.Actions
{
/// <summary>
/// Implements Keyboard actions for a specific element.
mialeska marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class KeyboardActions : IKeyboardActions
{
private readonly IElement element;
private readonly string elementType;
private readonly IApplication application;
private readonly LocalizationLogger localizationLogger;
private readonly ElementActionRetrier elementActionsRetrier;

/// <summary>
/// Instantiates Keyboard actions for a specific element.
/// </summary>
/// <param name="element">Target element.</param>
/// <param name="elementType">Target element's type.</param>
/// <param name="application">Current application session.</param>
/// <param name="localizationLogger">Logger for localized values.</param>
/// <param name="elementActionsRetrier">Retrier for element actions.</param>
public KeyboardActions(IElement element, string elementType, IApplication application, LocalizationLogger localizationLogger, ElementActionRetrier elementActionsRetrier)
{
this.element = element;
this.elementType = elementType;
this.application = application;
this.localizationLogger = localizationLogger;
this.elementActionsRetrier = elementActionsRetrier;
}

public void PressKey(string keyToPress)
{
LogElementAction("loc.keyboard.presskey", keyToPress.GetLoggableValueForKeyboardKey());
PerformAction((actions, element) => actions.KeyDown(element, keyToPress));
}

public void ReleaseKey(string keyToRelease)
{
LogElementAction("loc.keyboard.releasekey", keyToRelease.GetLoggableValueForKeyboardKey());
PerformAction((actions, element) => actions.KeyUp(element, keyToRelease));
}

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

public void SendKeysWithKeyHold(string keySequence, string keyToHold)
{
LogElementAction("loc.keyboard.sendkeys.withkeyhold", keySequence, keyToHold.GetLoggableValueForKeyboardKey());
PerformAction((actions, element) => actions.KeyDown(element, keyToHold).SendKeys(element, keySequence).KeyUp(element, keyToHold));
}

/// <summary>
/// Performs submited action against new <see cref="SeleniumActions"/> object.
/// </summary>
/// <param name="action">Action to be performed.</param>
protected virtual void PerformAction(Func<SeleniumActions, IWebElement, SeleniumActions> action)
{
elementActionsRetrier.DoWithRetry(() =>
{
action(new SeleniumActions(application.Driver), element.GetElement()).Build().Perform();
});
}

/// <summary>
/// Logs element 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 LogElementAction(string messageKey, params object[] args)
{
localizationLogger.InfoElementAction(elementType, element.Name, messageKey, args);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
using Aquality.Selenium.Core.Localization;
using Aquality.Selenium.Core.Utilities;
using Aquality.Selenium.Core.Waitings;
using Aquality.WinAppDriver.Actions;
using Aquality.WinAppDriver.Applications;
using Aquality.WinAppDriver.Elements.Interfaces;
using OpenQA.Selenium;
using KeyboardActions = Aquality.WinAppDriver.Elements.Actions.KeyboardActions;
using CoreElement = Aquality.Selenium.Core.Elements.Element;
using CoreElementFactory = Aquality.Selenium.Core.Elements.Interfaces.IElementFactory;
using CoreElementFinder = Aquality.Selenium.Core.Elements.Interfaces.IElementFinder;

namespace Aquality.WinAppDriver.Elements
{
public abstract class Element : CoreElement
public abstract class Element : CoreElement, IElement
{
protected Element(By locator, string name) : base(locator, name, ElementState.Displayed)
{
Expand All @@ -26,7 +28,9 @@ protected Element(By locator, string name) : base(locator, name, ElementState.Di

protected override CoreElementFactory Factory => CustomFactory;

protected IElementFactory CustomFactory => ApplicationManager.GetRequiredService<IElementFactory>();
protected virtual IElementFactory CustomFactory => ApplicationManager.GetRequiredService<IElementFactory>();

public virtual IKeyboardActions KeyboardActions => new KeyboardActions(this, ElementType, Application, LocalizationLogger, ActionRetrier);

public T FindChildElement<T>(By childLocator, ElementSupplier<T> supplier = null) where T : IElement
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using CoreElement = Aquality.Selenium.Core.Elements.Interfaces.IElement;
using Aquality.WinAppDriver.Actions;
using CoreElement = Aquality.Selenium.Core.Elements.Interfaces.IElement;

namespace Aquality.WinAppDriver.Elements.Interfaces
{
public interface IElement : CoreElement
{
IKeyboardActions KeyboardActions { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using OpenQA.Selenium;
using System.Linq;
using System.Reflection;

namespace Aquality.WinAppDriver.Extensions
{
public static class StringExtensions
{
/// <summary>
/// Returns name of the value from <see cref="Keys"/>, readable in the log.
/// </summary>
/// <param name="key">Keyboard key to define.</param>
/// <returns>Readable value.</returns>
public static string GetLoggableValueForKeyboardKey(this string key)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why it is public? It looks like helper for some library actions, but not as public method that should be available in client code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

don't see any reason why should we hide the functionality that could be used by the customer.
I can imagine situations when I want to log this value but I cannot.
Seems that I should rework this keys to public enum...

{
return typeof(Keys)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault(field => key.Equals(field.GetValue(null)))?.Name ?? key;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@
"loc.label": "Надпіс",
"loc.text.field": "Тэкставае поле",
"loc.text.clearing": "Aчышчаем",
"loc.text.typing": "Уводзім значэнне '{0}'"
"loc.text.typing": "Уводзім значэнне '{0}'",
"loc.keyboard.presskey": "Заціскаем клавішу '{0}'",
mialeska marked this conversation as resolved.
Show resolved Hide resolved
"loc.keyboard.releasekey": "Адпускаем клавішу '{0}'",
"loc.keyboard.sendkeys": "Націскаем клавішы '{0}'",
"loc.keyboard.sendkeys.withkeyhold": "Націскаем клавішы '{0}' з заціснутай клавішай '{1}'"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@
"loc.label": "Label",
"loc.text.field": "Text Field",
"loc.text.clearing": "Clearing",
"loc.text.typing": "Typing '{0}'"
"loc.text.typing": "Typing '{0}'",
"loc.keyboard.presskey": "Pressing key '{0}'",
"loc.keyboard.releasekey": "Releasing key '{0}'",
"loc.keyboard.sendkeys": "Sending keys '{0}'",
"loc.keyboard.sendkeys.withkeyhold": "Sending keys '{0}' with holded key '{1}'"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@
"loc.label": "Надпись",
"loc.text.field": "Текстовое поле",
"loc.text.clearing": "Очищаем",
"loc.text.typing": "Вводим значение '{0}'"
"loc.text.typing": "Вводим значение '{0}'",
"loc.keyboard.presskey": "Зажимаем клавишу '{0}'",
"loc.keyboard.releasekey": "Отпускаем клавишу '{0}'",
"loc.keyboard.sendkeys": "Нажимаем клавиши '{0}'",
"loc.keyboard.sendkeys.withkeyhold": "Нажимаем клавиши '{0}' с зажатой клавишей '{1}'"
}
Loading