Skip to content
This repository has been archived by the owner on Apr 30, 2022. It is now read-only.

Make lazy elements work with action builder, element handler and Selenium utilities #833

Merged
merged 9 commits into from
Nov 5, 2021
145 changes: 141 additions & 4 deletions Framework/BaseSeleniumTest/ActionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ public static class ActionBuilder
public static void HoverOver(this IWebDriver webDriver, By by)
{
Actions builder = new Actions(webDriver);
IWebElement element = webDriver.Wait().ForClickableElement(by);
IWebElement element = webDriver.Wait().ForElementExist(by);
builder.MoveToElement(element).Build().Perform();
}

/// <summary>
/// Performs a hover over on an element
/// </summary>
/// <param name="element">The web element</param>
public static void HoverOver(this IWebElement element)
{
Actions builder = new Actions(SeleniumUtilities.SearchContextToWebDriver(element));
builder.MoveToElement(element).Build().Perform();
}

Expand All @@ -46,8 +56,105 @@ public static void PressModifierKey(this IWebDriver webDriver, string key)
/// <param name="pixelsOffset">Integer of pixels to be moved (Positive or negative)</param>
public static void SlideElement(this IWebDriver webDriver, By element, int pixelsOffset)
{
Actions builder = new Actions(webDriver);
builder.DragAndDropToOffset(webDriver.FindElement(element), pixelsOffset, 0).Build().Perform();
IWebElement sourceElement = webDriver.Wait().ForElementExist(element);

sourceElement.DragAndDropToOffset(pixelsOffset, 0);
}

/// <summary>
/// Slider method which will take an offset of X pixels
/// </summary>
/// <param name="element">Element to be used</param>
/// <param name="pixelsOffset">Integer of pixels to be moved (Positive or negative)</param>
public static void SlideElement(this IWebElement element, int pixelsOffset)
{
element.DragAndDropToOffset(pixelsOffset, 0);
}

/// <summary>
/// Drag and drop an element
/// </summary>
/// <param name="webDriver">The IWebDriver</param>
/// <param name="source">Element to drag and drop</param>
/// <param name="destination">Where to drop the element</param>
public static void DragAndDrop(this IWebDriver webDriver, By source, By destination)
{
IWebElement sourceElement = webDriver.Wait().ForElementExist(source);
IWebElement destinationElement = webDriver.Wait().ForElementExist(destination);

sourceElement.DragAndDrop(destinationElement);
}

/// <summary>
/// Drag and drop an element
/// </summary>
/// <param name="source">Element to drag and drop</param>
/// <param name="destination">Where to drop the element</param>
public static void DragAndDrop(this IWebElement source, IWebElement destination)
{
Actions builder = new Actions(SeleniumUtilities.SearchContextToWebDriver(source));
builder.DragAndDrop(source, destination).Build().Perform();
}

/// <summary>
/// Drag and drop an element, plus or minus and X and Y offsets
/// </summary>
/// <param name="webDriver">The IWebDriver</param>
/// <param name="source">Element to drag and drop</param>
/// <param name="destination">Where to drop the element, plus or minus the offsets</param>
/// <param name="pixelsXOffset">Integer of pixels to be moved (Positive or negative) horizontally</param>
/// <param name="pixelsYOffset">Integer of pixels to be moved (Positive or negative) vertically </param>
public static void DragAndDropToOffset(this IWebDriver webDriver, By source, By destination, int pixelsXOffset, int pixelsYOffset)
{
IWebElement sourceElement = webDriver.Wait().ForElementExist(source);
IWebElement destinationElement = webDriver.Wait().ForElementExist(destination);

DragAndDropToOffset(sourceElement, destinationElement, pixelsXOffset, pixelsYOffset);
}

/// <summary>
/// Drag and drop an item to a destination, plus or minus and X and Y offsets
/// </summary>
/// <param name="source">Element to drag and drop</param>
/// <param name="destination">Where to drop the element, plus or minus the offsets</param>
/// <param name="pixelsXOffset">Integer of pixels to be moved (Positive or negative) horizontally</param>
/// <param name="pixelsYOffset">Integer of pixels to be moved (Positive or negative) vertically </param>
public static void DragAndDropToOffset(IWebElement source, IWebElement destination, int pixelsXOffset, int pixelsYOffset)
{
Actions builder = new Actions(SeleniumUtilities.SearchContextToWebDriver(source));

// Move to element goes to the top left corner so compensate for that
int horizontalOffset = (destination.Size.Width / 2) + pixelsXOffset;
int verticalOffset = (destination.Size.Height / 2) + pixelsYOffset;

builder.ClickAndHold(source).MoveToElement(destination, horizontalOffset, verticalOffset).Release().Build().Perform();
}

/// <summary>
/// Drag and drop an element to an X and Y offsets
/// </summary>
/// <param name="webDriver">The IWebDriver</param>
/// <param name="source">Element to drag and drop</param>
/// <param name="pixelsXOffset">Integer of pixels to be moved (Positive or negative) horizontally</param>
/// <param name="pixelsYOffset">Integer of pixels to be moved (Positive or negative) vertically </param>
public static void DragAndDropToOffset(this IWebDriver webDriver, By source, int pixelsXOffset, int pixelsYOffset)
{
IWebElement sourceElement = webDriver.Wait().ForElementExist(source);

sourceElement.DragAndDropToOffset(pixelsXOffset, pixelsYOffset);
}

/// <summary>
/// Drag and drop an element to an X and Y offsets
/// </summary>
/// <param name="source">Element to drag and drop</param>
/// <param name="pixelsXOffset">Integer of pixels to be moved (Positive or negative) horizontally</param>
/// <param name="pixelsYOffset">Integer of pixels to be moved (Positive or negative) vertically </param>
public static void DragAndDropToOffset(this IWebElement source, int pixelsXOffset, int pixelsYOffset)
{
Actions builder = new Actions(SeleniumUtilities.SearchContextToWebDriver(source));

builder.DragAndDropToOffset(source, pixelsXOffset, pixelsYOffset).Build().Perform();
}

/// <summary>
Expand All @@ -57,9 +164,39 @@ public static void SlideElement(this IWebDriver webDriver, By element, int pixel
/// <param name="by">By selector for the element</param>
public static void RightClick(this IWebDriver webDriver, By by)
{
Actions builder = new Actions(webDriver);
IWebElement element = webDriver.Wait().ForClickableElement(by);
element.RightClick();
}

/// <summary>
/// Performs a right-click on an element
/// </summary>
/// <param name="element">The web element</param>
public static void RightClick(this IWebElement element)
{
Actions builder = new Actions(SeleniumUtilities.SearchContextToWebDriver(element));
builder.ContextClick(element).Build().Perform();
}

/// <summary>
/// Performs a right-click on an element
/// </summary>
/// <param name="webDriver">The IWebDriver</param>
/// <param name="by">By selector for the element</param>
public static void DoubleClick(this IWebDriver webDriver, By by)
{
IWebElement element = webDriver.Wait().ForClickableElement(by);
element.DoubleClick();
}

/// <summary>
/// Performs a right-click on an element
/// </summary>
/// <param name="element">The web element</param>
public static void DoubleClick(this IWebElement element)
{
Actions builder = new Actions(SeleniumUtilities.SearchContextToWebDriver(element));
builder.DoubleClick(element).Build().Perform();
}
}
}
97 changes: 90 additions & 7 deletions Framework/BaseSeleniumTest/ElementHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@ public static class ElementHandler
/// <returns>Text of the selected option in drop down</returns>
public static string GetSelectedOptionFromDropdown(this ISearchContext searchContext, By by)
{
SelectElement select = new SelectElement(searchContext.Wait().ForClickableElement(by));
return GetSelectedOptionFromDropdown(searchContext.Wait().ForClickableElement(by));
}

/// <summary>
/// Get selected option from dropdown
/// </summary>
/// <param name="element">Web element</param>
/// <returns>Text of the selected option in drop down</returns>
public static string GetSelectedOptionFromDropdown(this IWebElement element)
{
SelectElement select = new SelectElement(element);
return select.SelectedOption.Text;
}

Expand All @@ -39,12 +49,11 @@ public static string GetSelectedOptionFromDropdown(this ISearchContext searchCon
/// <returns>List<string> of selected items in dropdown</string></returns>
public static List<string> GetSelectedOptionsFromDropdown(this ISearchContext searchContext, By by)
{
List<IWebElement> elements = null;
List<string> selectedItems = new List<string>();
SelectElement select = new SelectElement(searchContext.Wait().ForClickableElement(by));

// Get a list of IWebElement objects for all selected options from the dropdown
elements = (List<IWebElement>)select.AllSelectedOptions;
List<IWebElement> elements = (List<IWebElement>)select.AllSelectedOptions;

// Add the text of each element that is not null to the selectedItems list
foreach (IWebElement element in elements)
Expand All @@ -67,7 +76,18 @@ public static List<string> GetSelectedOptionsFromDropdown(this ISearchContext se
/// <returns>The text in the textbox</returns>
public static string GetElementAttribute(this ISearchContext searchContext, By by, string attribute = "value")
{
return searchContext.Wait().ForVisibleElement(by).GetAttribute(attribute);
return GetElementAttribute(searchContext.Wait().ForVisibleElement(by), attribute);
}

/// <summary>
/// Get the value of a specific attribute for an element
/// </summary>
/// <param name="element">Web element</param>
/// <param name="attribute">The attribute to get the value for</param>
/// <returns>The text in the textbox</returns>
public static string GetElementAttribute(this IWebElement element, string attribute = "value")
{
return element.GetAttribute(attribute);
}

/// <summary>
Expand All @@ -79,7 +99,16 @@ public static string GetElementAttribute(this ISearchContext searchContext, By b
public static void CheckCheckBox(this ISearchContext searchContext, By by, bool check)
{
IWebElement element = searchContext.Wait().ForClickableElement(by);
CheckCheckBox(element, check);
}

/// <summary>
/// Check or Uncheck a checkbox NOTE: If checkbox is already in desired state, this method takes no action
/// </summary>
/// <param name="element">Web element</param>
/// <param name="check">True to check the checkbox. False to uncheck the checkbox</param>
public static void CheckCheckBox(this IWebElement element, bool check)
{
if (check && !element.Selected)
{
element.Click();
Expand Down Expand Up @@ -133,7 +162,17 @@ public static void ClickButton(this ISearchContext searchContext, By by, bool wa
/// <param name="elementsTextToSelect">List items as strings to select from list box</param>
public static void SelectMultipleElementsFromListBox(this ISearchContext searchContext, By by, List<string> elementsTextToSelect)
{
SelectElement selectItem = new SelectElement(searchContext.Wait().ForClickableElement(by));
SelectMultipleElementsFromListBox(searchContext.Wait().ForClickableElement(by), elementsTextToSelect);
}

/// <summary>
/// Select multiple items from a list box
/// </summary>
/// <param name="element">Web element</param>
/// <param name="elementsTextToSelect">List items as strings to select from list box</param>
public static void SelectMultipleElementsFromListBox(this IWebElement element, List<string> elementsTextToSelect)
{
SelectElement selectItem = new SelectElement(element);

// Select all desired items in the listbox
foreach (string text in elementsTextToSelect)
Expand Down Expand Up @@ -234,6 +273,16 @@ public static void ScrollIntoView(this ISearchContext searchContext, By by)
ScrollIntoView(searchContext, element);
}

/// <summary>
/// JavaScript method to scroll an element into the view
/// </summary>
/// <param name="element">IWebElement</param>
public static void ScrollIntoView(this IWebElement element)
{
var driver = SeleniumUtilities.SearchContextToWebDriver(element);
driver.ScrollIntoView(element);
}

/// <summary>
/// JavaScript method to scroll an element into the view
/// </summary>
Expand All @@ -258,6 +307,19 @@ public static void ScrollIntoView(this ISearchContext searchContext, By by, int
ExecuteScrolling(searchContext, x, y);
}

/// <summary>
/// JavaScript method to scroll an element into the view
/// </summary>
/// <param name="element">Web element</param>
/// <param name="x">Horizontal direction</param>
/// <param name="y">Vertical direction</param>
public static void ScrollIntoView(this IWebElement element, int x, int y)
{
var driver = SeleniumUtilities.SearchContextToWebDriver(element);
ScrollIntoView(element);
ExecuteScrolling(driver, x, y);
}

/// <summary>
/// JavaScript method to scroll an element into the view
/// </summary>
Expand All @@ -280,10 +342,21 @@ public static void ExecuteScrolling(this ISearchContext searchContext, int x, in
/// <param name="by">By selector</param>
/// <param name="textToEnter">The string being entered into the text input field</param>
public static void SlowType(this ISearchContext searchContext, By by, string textToEnter)
{
SlowType(searchContext.Wait().ForClickableElement(by), textToEnter);
}

/// <summary>
/// Method to slowly type a string
/// Used for scenarios where normal SendKeys types too quickly and causes issues with a website
/// </summary>
/// <param name="element">Web element</param>
/// <param name="textToEnter">The string being entered into the text input field</param>
public static void SlowType(this IWebElement element, string textToEnter)
{
foreach (char singleLetter in textToEnter)
{
searchContext.Wait().ForClickableElement(by).SendKeys(singleLetter.ToString());
element.SendKeys(singleLetter.ToString());
System.Threading.Thread.Sleep(500);
}
}
Expand All @@ -296,10 +369,20 @@ public static void SlowType(this ISearchContext searchContext, By by, string tex
/// <param name="textToEnter">The string being entered into the text input field</param>
/// <param name="logger">The Logging object</param>
public static void SendSecretKeys(this ISearchContext searchContext, By by, string textToEnter, ILogger logger)
{
SendSecretKeys(searchContext.Wait().ForClickableElement(by), textToEnter, logger);
}

/// <summary>
/// Method used to send secret keys without logging
/// </summary>
/// <param name="secretElement">Web element</param>
/// <param name="textToEnter">The string being entered into the text input field</param>
/// <param name="logger">The Logging object</param>
public static void SendSecretKeys(this IWebElement secretElement, string textToEnter, ILogger logger)
{
try
{
IWebElement secretElement = searchContext.Wait().ForClickableElement(by);
logger.SuspendLogging();
secretElement.SendKeys(textToEnter);
logger.ContinueLogging();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace Magenic.Maqs.BaseSeleniumTest.Extensions
/// <summary>
/// Abstract structure for dynamically finding and interacting with elements
/// </summary>
public abstract class AbstractLazyIWebElement : IWebElement
public abstract class AbstractLazyIWebElement : IWebElement, IWrapsElement
{
/// <summary>
/// The index in cases where the selector finds multiple elements
Expand Down Expand Up @@ -318,6 +318,27 @@ public Size Size
}
}

/// <summary>
/// Gets the <see cref="IWebElement"/> wrapped by this object.
/// </summary>
public IWebElement WrappedElement
{
get
{
// Get the actual element
var element = this.GetRawExistingElement();

// If the element is wrapped get the lower level unwrapped version
if (element is IWrapsElement argAsWrapsElement)
{
return argAsWrapsElement.WrappedElement;
}

// This element can be used with things like action builders
return element;
}
}

/// <summary>
/// Click the lazy element
/// </summary>
Expand Down
Loading