Skip to content

Instantly share code, notes, and snippets.

@tsibiski
Created March 11, 2019 20:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tsibiski/04410e9646ee9ced9f3794266d6c5a82 to your computer and use it in GitHub Desktop.
Save tsibiski/04410e9646ee9ced9f3794266d6c5a82 to your computer and use it in GitHub Desktop.
using System;
using System.Linq;
using System.IO;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Support.Extensions;
using TestAutomation.Tests;
using TestAutomation.PageObjects;
using TestAutomation.Core.Utilities;
using System.Collections.Generic;
namespace TestAutomation
{
public static class Driver
{
public const int EXPLICIT_WAIT_DEFAULT = 10;
public const int LOADING_WAIT_DEFAULT = 60;
/// <summary>
/// Scroll provided distance, or to bottom of page if no distance is supplied.
/// </summary>
/// <param name="pixelDistance"></param>
public static void Scroll(this IWebDriver driver, int? pixelDistance = null) {
driver.ExecuteJavaScript($"window.scrollBy(0,{(pixelDistance != null ? pixelDistance.ToString() : "$(window).height()")})");
}
/// <summary>
/// This logic will find and click an element no matter where in the DOM it is, including within IFrames and Form tags, which Selenium is unreliable with. Requires page has JQuery.
/// </summary>
/// <param name="driver"></param>
/// <param name="cssSelector"></param>
public static void ReliableClick(this IWebDriver driver, string cssSelector, bool performClickWithJavascript = false)
{
driver.WaitUntil(() => driver.ElementIsVisibleAndInteractable(cssSelector), 10);
if (performClickWithJavascript) {
driver.ExecuteJavaScript(cssSelector.Contains("$") ? $"{cssSelector}.click()" : $"$('{cssSelector}').click()");
} else {
string xpath = GetFullyQualifiedXPathToElement(cssSelector);
if (string.IsNullOrEmpty(xpath)) {
Check.Fail(string.Format("Could not find element by selector {0}.", cssSelector));
}
try {
driver.FindElement(By.XPath(xpath)).ScrollTo();
driver.FindElement(By.XPath(xpath)).Click();
Check.Pass(string.Format("Click element --> selector {0}.", cssSelector));
} catch (Exception e) {
Check.Warn(string.Format("Could not click element [{0}] using Selenium. Performing JQuery click action.", e.Message));
BaseTest.Driver.ExecuteJavaScript(string.Format("$('{0}').click()", cssSelector.Replace("'", "\"")));
Check.Pass(string.Format("Click element --> selector {0}.", cssSelector));
}
}
}
public static void ReliableClick(string cssSelector, bool performClickWithJavascript = false)
{
BaseTest.Driver.WaitUntil(() => BaseTest.Driver.ElementIsVisibleAndInteractable(cssSelector), 10);
if (performClickWithJavascript) {
BaseTest.Driver.ExecuteJavaScript(string.Format("$('{0}').click()", cssSelector.Replace("'", "\"")));
} else {
string xpath = GetFullyQualifiedXPathToElement(cssSelector);
if (string.IsNullOrEmpty(xpath)) {
Check.Fail(string.Format("Could not find element by selector {0}.", cssSelector));
}
try {
BaseTest.Driver.FindElement(By.XPath(xpath)).ScrollTo();
BaseTest.Driver.FindElement(By.XPath(xpath)).Click();
Check.Pass(string.Format("Click element --> selector {0}.", cssSelector));
} catch (Exception e) {
Check.Warn(string.Format("Could not click element [{0}] using Selenium. Performing JQuery click action.", e.Message));
BaseTest.Driver.ExecuteJavaScript(string.Format("$('{0}').click()", cssSelector.Replace("'", "\"")));
Check.Pass(string.Format("Click element --> selector {0}.", cssSelector));
}
}
}
public static void ReliableClickAsFullJQuery(string jquery)
{
BaseTest.Driver.WaitUntil(() => BaseTest.Driver.ElementIsVisibleAndInteractable(jquery, true), 10);
string xpath = GetFullyQualifiedXPathToElement(jquery, true);
if (string.IsNullOrEmpty(xpath)) {
Check.Fail(string.Format("Could not find element with supplied jquery [{0}].", jquery));
}
try {
BaseTest.Driver.FindElement(By.XPath(xpath)).ScrollTo();
BaseTest.Driver.FindElement(By.XPath(xpath)).Click();
Check.Pass(string.Format("Click element --> selector {0}.", jquery));
} catch (Exception e) {
Check.Warn(string.Format("Could not click element [{0}] using Selenium. Performing JQuery click action.", e.Message));
BaseTest.Driver.ExecuteJavaScript(string.Format("{0}.click()", jquery));
Check.Pass(string.Format("Click element --> selector {0}.", jquery));
}
}
public static void ReliableClickAsFullJQuery(this IWebDriver driver, string jquery)
{
BaseTest.Driver.WaitUntil(() => driver.ElementIsVisibleAndInteractable(jquery, true), 10);
string xpath = GetFullyQualifiedXPathToElement(jquery, true);
if (string.IsNullOrEmpty(xpath)) {
Check.Fail(string.Format("Could not find element with supplied jquery [{0}].", jquery));
}
try {
driver.FindElement(By.XPath(xpath)).ScrollTo();
driver.FindElement(By.XPath(xpath)).Click();
Check.Pass(string.Format("Click element --> selector {0}.", jquery));
} catch (Exception e) {
Check.Warn(string.Format("Could not click element [{0}] using Selenium. Performing JQuery click action.", e.Message));
driver.ExecuteJavaScript(string.Format("{0}.click()", jquery));
Check.Pass(string.Format("Click element --> selector {0}.", jquery));
}
}
public static void ReliableClick(this IWebElement el) {
if (el == null) {
Check.Fail($"Click called on a null element.");
}
el.ScrollTo();
el.Click();
}
public static IWebElement ReliableFindElement(this IWebDriver driver, string cssSelector, bool isFullJQuery = false)
{
driver.WaitUntil(() => driver.ElementIsVisibleAndInteractable(cssSelector, isFullJQuery), 10);
try
{
return driver.FindElement(By.XPath(GetFullyQualifiedXPathToElement(cssSelector, isFullJQuery)));
}
catch (Exception e) {
Check.Fail(string.Format("{0} -- CssSelector was {1}", e.Message, cssSelector));
return null;
}
}
/// <summary>
/// Find all elements on page matching selector.
/// </summary>
public static List<IWebElement> ReliableFindElements(this IWebDriver driver, string cssSelector, bool isFullJQuery = false)
{
driver.WaitUntil(() => driver.ElementIsVisibleAndInteractable(cssSelector, isFullJQuery), 10);
try
{
List<string> xpaths = new List<string>();
string reformattedCssSelector = cssSelector.Replace("'", "\"");
int count = Convert.ToInt32(driver.ExecuteJavaScript<object>(isFullJQuery ? $"return {cssSelector}.length;" : $"return $('{reformattedCssSelector}').length;"));
for (int x = 0; x < count; x++) {
xpaths.Add(GetFullyQualifiedXPathToElement(isFullJQuery ? $"$({cssSelector}[{x}])" : $"$($('{reformattedCssSelector}')[{x}])", isFullJQuery));
}
List<IWebElement> elements = new List<IWebElement>();
foreach (string xpath in xpaths) {
elements.Add(driver.FindElement(By.XPath(xpath)));
}
return elements;
}
catch (Exception e)
{
Check.Fail(string.Format("{0} -- CssSelector was {1}", e.Message, cssSelector));
return new List<IWebElement>();
}
}
public enum ContainsTextModifer { Contains, StartsWith, EndsWith }
public static IWebElement ReliableFindElementContainingText(this IWebDriver driver, string containsText, string restrictElementTagTo = "", string parentSelectorToSearchUnder = "", ContainsTextModifer containsTextModifer = ContainsTextModifer.Contains, bool lastMatch = false)
{
string cssSelector = string.IsNullOrEmpty(parentSelectorToSearchUnder) ? "$" : $"$('{parentSelectorToSearchUnder}').find";
string equation = string.Empty;
switch (containsTextModifer) {
case ContainsTextModifer.Contains:
equation = ">= 0";
break;
case ContainsTextModifer.StartsWith:
equation = "== 0";
break;
case ContainsTextModifer.EndsWith:
equation = $"== {cssSelector}(\"{restrictElementTagTo}:contains('{containsText}')\").length - 1";
break;
}
cssSelector = $"{cssSelector}(\"{restrictElementTagTo}:contains('{containsText}')\").filter(function(i,el) {{ return $(el).text().indexOf('{containsText}') {equation}; }}).first()";
driver.WaitUntil(() => driver.ElementIsVisibleAndInteractable(cssSelector, true), 10);
try
{
return driver.FindElement(By.XPath(GetFullyQualifiedXPathToElement(cssSelector, true)));
}
catch (Exception e)
{
Check.Fail(string.Format("{0} -- Contains text was {1}", e.Message, containsText));
return null;
}
}
/// <summary>
/// This method will select element from a dropdown based on index
/// </summary>
/// <param name="DropdownElementName"></param>
/// <param name="index"></param>
public static void SelectFromDropdown(this IWebDriver driver, string selector, int index)
{
BasePage.CurrentPage.WaitForNoLoading();
BaseTest.Driver.ExecuteJavaScript(string.Format("$('{0}').find('option').prop('selected', false); $($('{0}').find('option')[{1}]).prop('selected', true);", selector, index));
Check.Pass(string.Format("Click dropdown --> selector {0}, index {1}.", selector, index));
}
public static void SelectFromDropdown(this IWebDriver driver, IWebElement DropdownElement, int index)
{
SelectElement selector = new SelectElement(DropdownElement);
selector.SelectByIndex(index);
Check.Pass(string.Format("Click dropdown --> selector {0}, index {1}.", DropdownElement.Text, index));
}
/// <summary>
/// /// This method will select element from a dropdown based on value
/// </summary>
/// <param name="DropdownElementName"></param>
/// <param name="value"></param>
public static void SelectFromDropdownById(this IWebDriver driver, string selector, string dropdownValue)
{
BasePage.CurrentPage.WaitForNoLoading();
BaseTest.Driver.ReliableFindElement(selector).Click();
string selectorFormatted = string.Format("$(\"{0}\").find(\"option[value='{1}']\")", selector, dropdownValue);
if (!BaseTest.Driver.ElementExists(selectorFormatted, true) && (dropdownValue.Contains("/") || dropdownValue.ToLower() == "today")) {
if (dropdownValue.ToLower() != "today") {
Check.Warn(string.Format("Did not find element with selector \"{0}\" and drowpdown value of {1}. Searching for a date selector matching \"Today\".", selector, dropdownValue));
}
selectorFormatted = string.Format("$(\"{0}\").find(\"option:contains('Today')\")", selector);
}
BaseTest.Driver.ExecuteJavaScript($"{selectorFormatted}.attr('selected',true)");
BaseTest.Driver.ExecuteJavaScript($"$(\"{selector}\").trigger(\"change\")");
BaseTest.Driver.ReliableFindElement(".user-bar-wrapper").Click();
BasePage.CurrentPage.WaitForNoLoading();
Check.Pass(string.Format("Click dropdown --> selector {0}, value {1}.", selector, dropdownValue));
}
public static void SelectFromDropdownById(this IWebDriver driver, IWebElement DropdownElement, string dropdownValue)
{
SelectElement selector = new SelectElement(DropdownElement);
selector.SelectByValue(dropdownValue);
}
public static void SelectFromDropdownByText(this IWebDriver driver, string selector, string dropdownValue)
{
BasePage.CurrentPage.WaitForNoLoading(); ;
BaseTest.Driver.ReliableFindElement(selector).Click();
string selectorFormatted = string.Format("$(\"{0}\").find(\"option[text='{1}']\")", selector, dropdownValue);
if (!BaseTest.Driver.ElementExists(selectorFormatted, true) && (dropdownValue.Contains("/") || dropdownValue.ToLower() == "today")) {
if (dropdownValue.ToLower() != "today") {
Check.Warn(string.Format("Did not find element with selector \"{0}\" and drowpdown value of {1}. Searching for a date selector matching \"Today\".", selector, dropdownValue));
}
selectorFormatted = string.Format("$(\"{0}\").find(\"option:contains('Today')\")", selector);
}
BaseTest.Driver.ExecuteJavaScript($"{selectorFormatted}.attr('selected',true)");
BaseTest.Driver.ExecuteJavaScript($"$(\"{selector}\").trigger(\"change\")");
BaseTest.Driver.ReliableFindElement(".user-bar-wrapper").Click();
BasePage.CurrentPage.WaitForNoLoading();
Check.Pass(string.Format("Click dropdown --> selector {0}, value {1}.", selector, dropdownValue));
}
public static void SelectFromDropdownByText(this IWebDriver driver, IWebElement DropdownElement, string displayedtext)
{
SelectElement selector = new SelectElement(DropdownElement);
selector.SelectByText(displayedtext);
Check.Pass(string.Format("Click dropdown --> selector {0}, value {1}.", DropdownElement.Text, displayedtext));
}
public static void WaitForNoLoading(this BasePage page)
{
float timer = LOADING_WAIT_DEFAULT;
try {
while (BaseTest.Driver.ExecuteJavaScript<bool>("return typeof $('.blockUI') != 'undefined' && $('.blockUI').is(':visible') && document.readyState == 'complete'") && timer > 0f) {
System.Threading.Thread.Sleep(500);
timer -= 0.5f;
}
} catch {
System.Threading.Thread.Sleep(500);
return;
}
if (timer <= 0f) {
Check.Fail(string.Format("Timed out waiting for loading overlay to dismiss on the {0} Page. Wait time max is {1}.", page.PageName, LOADING_WAIT_DEFAULT));
}
//If page fails to load at all, mark inconclusive to trigger a re-run.
timer = LOADING_WAIT_DEFAULT;
while (!BaseTest.Driver.ElementExists("body") && timer > 0f) {
System.Threading.Thread.Sleep(500);
timer = timer - 0.5f;
}
if (timer <= 0f) {
Check.Inconclusive("Page failed to load the DOM. Marking inconclusive to trigger a re-run.");
}
System.Threading.Thread.Sleep(500);
}
/// <summary>
/// Selenium sometimes has trouble finding elements on the page. Give it some help by using JQuery to grab the full qualified xpath to that element.
/// </summary>
/// <param name="cssSelector"></param>
/// <returns></returns>
public static string GetFullyQualifiedXPathToElement(string cssSelector, bool isFullJQuery = false, bool noWarn = false)
{
if (cssSelector.Contains("$(") && !isFullJQuery) {
isFullJQuery = true;
}
string finder_method = @"
function getPathTo(element) {
if(typeof element == 'undefined') return '';
if (element.tagName == 'HTML')
return '/HTML[1]';
if (element===document.body)
return '/HTML[1]/BODY[1]';
var ix= 0;
var siblings = element.parentNode.childNodes;
for (var i= 0; i< siblings.length; i++) {
var sibling= siblings[i];
if (sibling===element)
return getPathTo(element.parentNode)+'/'+element.tagName+'['+(ix+1)+']';
if (sibling.nodeType===1 && sibling.tagName===element.tagName)
ix++;
}
}
";
if(isFullJQuery) {
cssSelector = cssSelector.TrimEnd(';');
}
string executable = isFullJQuery ? string.Format("{0} return getPathTo({1}[0]);", finder_method, cssSelector) : string.Format("{0} return getPathTo($('{1}')[0]);", finder_method, cssSelector.Replace("'", "\""));
string xpath = string.Empty;
try {
xpath = BaseTest.Driver.ExecuteJavaScript<string>(executable);
} catch (Exception e) {
if (!noWarn) {
Check.Warn(string.Format("Exception occurred while building a dynamic Xpath. Css selector supplied to locate element is \"{0}\". Exception [{1}].", cssSelector, e.Message));
}
}
if (!noWarn && string.IsNullOrEmpty(xpath)) {
Check.Warn(string.Format("Supplied cssSelector did not point to an element. Selector is \"{0}\".", cssSelector));
}
return xpath;
}
public static bool ElementExists(this IWebDriver driver, string cssSelector, bool isFullQuery = false)
{
string xpath = string.Empty;
try {
xpath = GetFullyQualifiedXPathToElement(cssSelector, isFullQuery, true);
} catch { }
if (string.IsNullOrEmpty(xpath)) {
return false;
}
return driver.FindElements(By.XPath(xpath)).Count > 0;
}
public static bool ElementIsVisibleAndInteractable(this IWebDriver driver, string cssSelector, bool isFullQuery = false)
{
string xpath = string.Empty;
try {
xpath = GetFullyQualifiedXPathToElement(cssSelector, isFullQuery, true);
} catch { }
if (string.IsNullOrEmpty(xpath)) {
return false;
}
return driver.ElementExists(cssSelector, isFullQuery) && driver.FindElement(By.XPath(GetFullyQualifiedXPathToElement(cssSelector, isFullQuery))).Enabled && (isFullQuery || cssSelector.Contains("$") ? driver.ExecuteJavaScript<bool>($"return {cssSelector}.is(':visible');") : driver.ExecuteJavaScript<bool>($"return $(\"{cssSelector.Replace("\"", "'")}\").is(':visible');"));
}
public static bool IsReady(this IWebElement element)
{
return element != null && element.Enabled;
}
public static void Wait(this IWebDriver driver, float time)
{
driver.WaitUntil(() => false, time);
}
public static void WaitUntil(this IWebDriver driver, Func<bool> Condition, float timeout = 10f)
{
float timer = timeout;
while (!Condition.Invoke() && timer > 0f) {
System.Threading.Thread.Sleep(500);
timer -= 0.5f;
}
BasePage.CurrentPage.WaitForNoLoading();
System.Threading.Thread.Sleep(500);
}
public static void WaitUntil(this IWebElement element, Func<bool> Condition, float timeout = 10f)
{
float timer = timeout;
while (!Condition.Invoke() && timer > 0f) {
System.Threading.Thread.Sleep(500);
timer -= 0.5f;
}
BasePage.CurrentPage.WaitForNoLoading();
System.Threading.Thread.Sleep(500);
}
public static string GetDownloadedFileTextAndDelete(string fileName, bool deleteAfterRead = true) {
System.Threading.Thread.Sleep(500);
List<string> fileNameContains = fileName.Split('*').ToList();
List<string> matches = new List<string>();
string path = Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "Downloads");
string filePath = string.Empty;
if (fileNameContains.Count > 1)
{
BaseTest.Driver.WaitUntil(() => {
bool match = false;
string[] files = Directory.GetFiles(path);
foreach (string file in files) {
if (fileNameContains.FindAll(f => file.Contains(f)).Count == fileNameContains.Count && file.EndsWith(fileNameContains.Last())) {
DateTime creation = File.GetCreationTime(Path.Combine(path, file));
if (Math.Abs(creation.Subtract(DateTime.Now).TotalMinutes) < 2)
{
matches.Add(Path.Combine(path, file));
match = true;
}
else
{
File.Delete(Path.Combine(path, file));
}
}
}
return match;
}, 30);
}
else
{
BaseTest.Driver.WaitUntil(() => File.Exists(path), 30);
DateTime creation = File.GetCreationTime(Path.Combine(path, fileName));
if (Math.Abs(creation.Subtract(DateTime.Now).TotalMinutes) < 2)
{
matches.Add(Path.Combine(path, fileName));
}
else
{
File.Delete(Path.Combine(path, fileName));
}
}
if (!matches.Any()) {
Check.Fail("No files were downloaded recently that match the requested file name and extension.");
return string.Empty;
}
string fileText = string.Empty;
fileText = File.ReadAllText(matches.First());
if(deleteAfterRead) File.Delete(matches.First());
return fileText;
}
public static void ScrollTo(this IWebElement el)
{
BaseTest.Driver.ExecuteJavaScript("arguments[0].scrollIntoView(true);", el);
BaseTest.Driver.WaitUntil(el.IsReady);
}
}
}
public class InvalidJqueryException : Exception {
public override string Message { get; }
public InvalidJqueryException(string error) {
Message = error;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment