Skip to content
Merged
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
99 changes: 99 additions & 0 deletions Aquality.Selenium/src/Aquality.Selenium/Elements/ElementFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using Aquality.Selenium.Browsers;
using Aquality.Selenium.Configurations;
using Aquality.Selenium.Elements.Interfaces;
using Aquality.Selenium.Waitings;
using OpenQA.Selenium;

namespace Aquality.Selenium.Elements
{
internal sealed class ElementFinder : IElementFinder
{
private static readonly ThreadLocal<ElementFinder> InstanceHolder = new ThreadLocal<ElementFinder>();

private ElementFinder()
{
}

public static ElementFinder Instance
{
get
{
if (!InstanceHolder.IsValueCreated)
{
InstanceHolder.Value = new ElementFinder();
}

return InstanceHolder.Value;
}
}

private ITimeoutConfiguration TimeoutConfiguration => Configuration.Instance.TimeoutConfiguration;

private TimeSpan DefaultTimeout => TimeoutConfiguration.Condition;

private Browser Browser => BrowserManager.Browser;

public IWebElement FindElement(By locator, TimeSpan? timeout = null, ElementState state = ElementState.ExistsInAnyState)
{
var clearTimeout = timeout ?? DefaultTimeout;
var elements = FindElements(locator, clearTimeout, state);
if (elements.Any())
{
return elements.First();
}

throw new NoSuchElementException($"Element was not found in {clearTimeout.Seconds} seconds in state {state} by locator {locator}");
}

public IWebElement FindElement(By by)
{
return FindElement(by, timeout: null);
}

public ReadOnlyCollection<IWebElement> FindElements(By locator, TimeSpan? timeout = null, ElementState state = ElementState.ExistsInAnyState)
{
var resultElements = new List<IWebElement>();
Browser.ImplicitWaitTimeout = TimeSpan.Zero;
ConditionalWait.WaitForTrue(driver =>
{
var foundElements = driver.FindElements(locator);
var filteredElements = FilterByState(foundElements, state);
resultElements.AddRange(filteredElements);
return filteredElements.Any();
}, timeout ?? DefaultTimeout);
Browser.ImplicitWaitTimeout = TimeoutConfiguration.Implicit;
return resultElements.ToList().AsReadOnly();
}

private IList<IWebElement> FilterByState(IList<IWebElement> foundElements, ElementState state)
{
var filteredElements = new List<IWebElement>();
if (foundElements.Any())
{
switch (state)
{
case ElementState.Displayed:
filteredElements.AddRange(foundElements.Where(element => element.Displayed));
break;
case ElementState.ExistsInAnyState:
filteredElements.AddRange(foundElements);
break;
default:
throw new InvalidOperationException($"{state} state is not recognized");
}
}

return filteredElements;
}

public ReadOnlyCollection<IWebElement> FindElements(By by)
{
return FindElements(by, timeout: null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using OpenQA.Selenium;
using System;
using System.Collections.ObjectModel;

namespace Aquality.Selenium.Elements.Interfaces
{
/// <summary>
/// Provides ability to find elements in desired ElementState
/// </summary>
public interface IElementFinder : ISearchContext
{
/// <summary>
/// Finds elements in desired ElementState
/// </summary>
/// <param name="locator">elements locator</param>
/// <param name="timeout">timeout for search</param>
/// <param name="state">desired ElementState</param>
/// <returns>List of found elements</returns>
ReadOnlyCollection<IWebElement> FindElements(By locator, TimeSpan? timeout = null, ElementState state = ElementState.ExistsInAnyState);

/// <summary>
/// Finds element in desired ElementState
/// </summary>
/// <param name="locator">elements locator</param>
/// <param name="timeout">timeout for search</param>
/// <param name="state">desired ElementState</param>
/// <exception cref="OpenQA.Selenium.NoSuchElementException">Thrown if element was not found in time in desired state</exception>
/// <returns>Found element</returns>
IWebElement FindElement(By locator, TimeSpan? timeout = null, ElementState state = ElementState.ExistsInAnyState);
}
}
100 changes: 94 additions & 6 deletions Aquality.Selenium/src/Aquality.Selenium/Waitings/ConditionalWait.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,104 @@
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Text;
using Aquality.Selenium.Browsers;
using Aquality.Selenium.Configurations;
using Aquality.Selenium.Logging;
using OpenQA.Selenium.Support.UI;

namespace Aquality.Selenium.Waitings
{
/// <summary>
/// This class is using for waiting any conditions.
/// </summary>
public static class ConditionalWait
{
public static bool WaitForTrue(Func<IWebDriver, bool> condition)
private static readonly IConfiguration Configuration = Configurations.Configuration.Instance;

/// <summary>
/// Wait for some object from condition with timeout.
/// Default timeout(<see cref="ITimeoutConfiguration.Condition"/>) is using.
/// </summary>
/// <typeparam name="T">Type of object which is waiting</typeparam>
/// <param name="condition">Function for waiting</param>
/// <param name="timeOut">Time-out</param>
/// <returns>Object which waiting for or default of a T class - is exceptions occured</returns>
public static T WaitFor<T>(Func<IWebDriver, T> condition, TimeSpan? timeOut = null)
{
BrowserManager.Browser.ImplicitWaitTimeout = TimeSpan.Zero;
var wait = new DefaultWait<IWebDriver>(BrowserManager.Browser.Driver)
{
Timeout = ResolveConditionTimeOut(timeOut),
PollingInterval = Configuration.TimeoutConfiguration.PollingInterval
};
wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException), typeof(NoSuchElementException));

try
{
return wait.Until(condition);
}
catch (Exception e)
{
Logger.Instance.Debug("Aquality.Selenium.Waitings.ConditionalWait.WaitFor", e);
}
finally
{
BrowserManager.Browser.ImplicitWaitTimeout = Configuration.TimeoutConfiguration.Implicit;
}
return default(T);
}

/// <summary>
/// Wait for condition and return true if waiting successful or false - otherwise.
/// Default timeout(<see cref="ITimeoutConfiguration.Condition"/>) is using.
/// </summary>
/// <param name="condition">Function for waiting</param>
/// <param name="timeOut">Time-out</param>
/// <returns>True if waiting successful or false - otherwise.</returns>
public static bool WaitForTrue(Func<IWebDriver, bool> condition, TimeSpan? timeOut = null)
{
try
{
return WaitFor(condition, ResolveConditionTimeOut(timeOut));
}
catch (Exception e)
{
Logger.Instance.Debug("Aquality.Selenium.Waitings.ConditionalWait.WaitForTrue", e);
return false;
}
}

/// <summary>
/// For waiting without WebDriver: Wait for function will be true or return some except false.
/// Default timeout(<see cref="ITimeoutConfiguration.Condition"/>) is using.
/// </summary>
/// <typeparam name="T">Type of waitWith param <see cref="DefaultWait{T}"/></typeparam>
/// <typeparam name="TResult">Type of object which is waiting</typeparam>
/// <param name="condition">Function for waiting</param>
/// <param name="waitWith">Object who will helping to wait (which will be passed to <see cref="DefaultWait{T}"/>)</param>
/// <param name="timeOut">Time-out</param>
/// <returns>Object which waiting for or default of a TResult class - is exceptions occured</returns>
public static TResult WaitFor<T, TResult>(Func<T, TResult> condition, T waitWith, TimeSpan? timeOut = null)
{
var wait = new DefaultWait<T>(waitWith)
{
Timeout = ResolveConditionTimeOut(timeOut),
PollingInterval = Configuration.TimeoutConfiguration.PollingInterval
};

try
{
return wait.Until(condition);
}
catch (Exception e)
{
Logger.Instance.Debug("Aquality.Selenium.Waitings.ConditionalWait.WaitFor", e);
}
return default(TResult);
}

private static TimeSpan ResolveConditionTimeOut(TimeSpan? timeOut)
{
throw new NotImplementedException();
return timeOut ?? Configuration.TimeoutConfiguration.Condition;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using Aquality.Selenium.Browsers;
using NUnit.Framework;
using System;
using System.Linq;
using Aquality.Selenium.Browsers;
using NUnit.Framework;

namespace Aquality.Selenium.Tests.JavaScripts
namespace Aquality.Selenium.Tests.Unit.JavaScripts
{
[Parallelizable(ParallelScope.All)]
public class JavaScriptTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using NLog.Targets;
using NUnit.Framework;

namespace Aquality.Selenium.Tests.Logging
namespace Aquality.Selenium.Tests.Unit.Logging
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
Expand Down