So I have this issue with explicit waits. I don't want to use Thread.Sleep(). This is an simple test which it opens a page and then goes back and forward. It takes about 2-3 seconds to load this page and I want to do this in a dynamic way (testing). Hope I am not too confusig. I did a lot of research but nothing works, maybe I am doing something wrong. ( I'm using Resharper to run unit tests)
Here I have also the solution:
https://www.dropbox.com/s/1k5vjc5czvmdd3u/ConsoleApplication2.rar?dl=0
I am using an extension to FindElement method so I believe it would be easy to just call this method and wait by itself. I have some explanation commented in the solution. I would appreciate if somebody give me some help. (Sorry for not so perfect english).
using System;
using System.Threading;
using ConsoleApplication2.Extensions;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
namespace ConsoleApplication2
{
class UnitTest1
{
private IWebDriver driver = new ChromeDriver();
[SetUp]
public void Initialize()
{
driver.Url = "some url";
Thread.Sleep(3500);
// driver.Manage().Timeouts().ImplicitlyWait(TimeSpan(20));
}
[Test]
public void CheckBackForward()
{
//Go to first page in Online Help
driver.FindElement(By.XPath("//span/ul/li/span/a")).Click();
// So if I am commenting this Thread.Sleep
// it will throw an exception at the extension method at line 13 at "#by"
// I've also checked this without extension method but still the same problem. It doesn'w wait at all, it will throw this
// exception as soon as FindElement method is called.
// I know that I shouldn't mix explicit waits with implicit ones.
//Thread.Sleep(1500);
//Store title
var title_text = driver.FindElement(By.XPath("//div[#id='shellAreaContent']/div/div[2]/ol/li[3]/span"),60).Text;
//Check if Back is enabled
Assert.IsTrue(driver.FindElement(By.XPath("//div[3]/a/span")).Enabled);
//Go back
driver.FindElement(By.XPath("//div[3]/a/span")).Click();
//Check if Forward is enabled
Assert.IsTrue(driver.FindElement(By.XPath("//div[4]/a/span")).Enabled);
//Go forward
driver.FindElement(By.XPath("//div[4]/a/span")).Click();
//Store title
var title_text2 = driver.FindElement(By.XPath("//div[#id='shellAreaContent']/div/div[2]/ol/li[3]/span")).Text;
//Check if you are on the same page
Assert.AreEqual(title_text2,title_text);
}
[TearDown]
public void EndTest()
{
driver.Quit();
}
}
}
And here is the extension:
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
namespace ConsoleApplication2.Extensions
{
public static class Extension
{
public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
{
if (timeoutInSeconds <= 0) return driver.FindElement(#by);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
return wait.Until(drv => drv.FindElement(#by));
}
}
}
I am coding with Selenium for 6+ months and I had the same problem as yours. I have created this extension method and it works for me every time.
What the code does is:
During 20 seconds, it checks each 500ms, whether or not the element is present on the page. If after 20 seconds, it's not found, it will throw an exception.
This will help you make a dynamic wait.
public static class SeleniumExtensionMethods {
public static WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
public static void SafeClick(this IWebElement webElement) {
try {
wait.Until(ExpectedConditions.ElementToBeClickable(webElement)).Click();
} catch (TargetInvocationException ex) {
Console.WriteLine(ex.InnerException);
}
}
and then replace this code of yours:
driver.FindElement(By.XPath("//span/ul/li/span/a")).Click();
with:
IWebElement x = driver.FindElement(By.XPath("//span/ul/li/span/a"));
x.SafeClick();
Related
I am building a testing tool for a popular hardware shop website in Australia. I want my test to go to a page that contains, let's say for example, 36 results (The page actually says "Showing 36 of 151 results") and click in the "add to cart" button in one randomly chosen product. To do this I am trying with driver.FindElements to capture all the 36 buttons in the page using a CssSelector.
The problem I am facing is that i can only get the first 12 items because the rest 24 are wrapped in a class=lazyload-wrapper and I just can't get the driver to capture them.
I have tried scrolling the page to the bottom, I have tried inserting Thread.Sleep everywhere, I have tried with WebDriverWait, I have also recreated some deprecated functions from ExpectedConditions to make it work but I am still only getting 12 elements.
I have read pretty much all of the posts about similar issues here and in other forums but I am still stuck with the same 12 items. Perhaps Selenium is not the best choice to do this. If anyone can throw some light with on this challenge it will be greatly appreciated.
This is the class I am using to interact with the page, please forgive me for leaving all that commented out code, it's just to show all the failed attempts:
using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using System.Collections.ObjectModel;
using System.Linq;
namespace Model.Pages
{
public class CarAccessoriesPage : BasePage
{
public CarAccessoriesPage(IWebDriver driver) : base(driver)
{
}
/// <summary>
/// Add a random product to the shopping cart.
/// </summary>
public CarAccessoriesPage AddRandomItemToCart()
{
// smooth scrolling down to force loading did not work
// IJavaScriptExecutor jsex = (IJavaScriptExecutor)driver;
// jsex.ExecuteScript("window.scrollTo({left:0, top:document.body.scrollHeight, behavior:'smooth'});");
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
// Scroll
IJavaScriptExecutor jsex = (IJavaScriptExecutor)driver;
jsex.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
// maybe wait for the page to load? Did not work.
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));
// Maybe inserting an ugly pause... no bueno
Thread.Sleep(3000);
// driver.FindElement(By.CssSelector("[data-index='35']")); // this element is not found
Random r = new Random();
int i = r.Next(0, GetPartialResultsCount());
// First approach throws OutOfRangeException because only 12 elements are found
// IWebElement itemElement = new WebDriverWait(driver, TimeSpan.FromSeconds(5))
// .Until(d => d.FindElements(By.CssSelector("[data-locator='atcButton']")))[i];
// Implementing a function from ExpectedConditions deprecated in C#, no good enough
var elements = wait
.Until(PresenceOfAllElementsLocatedBy(By.CssSelector("[data-locator='atcButton']")));
// maybe trying access the article elements inside the wrapper... fail
// var elements = driver.FindElements(By.CssSelector("article.lazyload-wrapper "));
Console.WriteLine(elements.Count); // Always 12!!! I know there are better ways to log
IWebElement ranItem = elements[i]; // Throws an OutOfRangeException if i > 11
Thread.Sleep(1000);
// center the element so the tob and bottom banners don intercept the click
jsex.ExecuteScript("arguments[0].scrollIntoView({block: 'center'});", ranItem);
Thread.Sleep(1000);
ranItem.Click();
Thread.Sleep(1000); // important or last function in test does not work (assertion fails: expected != actual)
return this;
}
/// <summary>
/// Get the number of items in the shopping cart.
/// </summary>
public int GetCartCount()
{
return Int32.Parse(driver.FindElement(By.ClassName("cartItemCount")).Text);
}
private Func<IWebDriver, ReadOnlyCollection<IWebElement>> PresenceOfAllElementsLocatedBy(By locator)
{
return (driver) =>
{
try
{
var elements = driver.FindElements(locator);
return elements.Any() ? elements : null;
}
catch (StaleElementReferenceException)
{
return null;
}
};
}
/// <summary>
/// Generate a random int no larger than the Collection of products
/// in the page. But due to lazy loading, the number generated was causing an out of range exception.
/// </summary>
private int GetPartialResultsCount()
{
string partialResultsCount = driver.FindElement(By.CssSelector(".resultsCount > p")).Text;
return Int32.Parse(Regex.Match(partialResultsCount, #"\d+").Value);
}
}
}
The test itself is actually very straight forward:
using NUnit.Framework;
using Model.Pages;
namespace Tests
{
public class ShoppingCartTests : BaseTests
{
[Test]
public void ValidateCartItemsCount()
{
int cartCount = open<HomePage>()
.GoToCarAccessoriesPage()
.AddRandomItemToCart()
.AddRandomItemToCart()
.GetCartCount();
// Validate cart has 2 items
Assert.AreEqual(2, cartCount);
}
}
}
Finally I just probably need to mention that the test will run and work if I only consider 12 items, but it has become like a personal obsession to get all the items that a user could see in the page. Also, no sure if I should share the actual website I am testing or this goes against the community rules or best practice. Probably is enough to say that is the most popular hardware shop in Australia and the website is fairly complex.
Thanks.
Maybe find the wrapped elements using an xpath selector like
"//*[contains(#class, 'lazyload-wrapper')]//button[contains(#data-locator, 'atcButton')]"
And then add the result to the one you usually get?
So, after many tries I found a way to make the test work and capture the elements wrapped in the lazyloader div. It's not very elegant and I am not exactly sure why do I need to scroll twice to the browser to actually perform the scrolling, and also I am not sure why the elements go missing if I remove one the pauses. By the way, the pauses I am using are simple Thread.Sleep(1000) wrapped in a helper class. Here is the code:
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System.Collections.ObjectModel;
using System.Linq;
using System.Diagnostics;
using Model.Helpers;
namespace Model.Pages
{
public class CarAccessoriesPage : BasePage
{
public CarAccessoriesPage(IWebDriver driver) : base(driver)
{
}
/// <summary>
/// Add a random product to the shopping cart.
/// </summary>
public CarAccessoriesPage AddRandomItemToCart()
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
IJavaScriptExecutor jsex = (IJavaScriptExecutor)driver;
Random r = new Random();
// Wait for the page to load.
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));
// Scroll
jsex.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
// Slow down
BrowserHelper.Pause();
// Scroll again
jsex.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
// Brake
BrowserHelper.Pause();
// Get all the 'add to cart' button elements
var elements = wait
.Until(PresenceOfAllElementsLocatedBy(By.CssSelector("[data-locator='atcButton']")));
// Test output
Trace.WriteLine($"Total products found: {elements.Count}");
// Select a random item
IWebElement ranItem = elements[r.Next(0, elements.Count)];
// Brake
BrowserHelper.Pause();
// Center the element so the top and bottom banners don't intercept the click
jsex.ExecuteScript("arguments[0].scrollIntoView({block: 'center'});", ranItem);
// Brake a little
BrowserHelper.Pause();
// Click 'add to cart'
ranItem.Click();
// Brake again. Important or last function in test does not work (assertion fails: expected != actual)
BrowserHelper.Pause();
return this;
}
/// <summary>
/// Get the number of items in the shopping cart.
/// </summary>
public int GetCartCount()
{
return Int32.Parse(driver.FindElement(By.ClassName("cartItemCount")).Text);
}
private Func<IWebDriver, ReadOnlyCollection<IWebElement>> PresenceOfAllElementsLocatedBy(By locator)
{
return (driver) =>
{
try
{
var elements = driver.FindElements(locator);
return elements.Any() ? elements : null;
}
catch (StaleElementReferenceException)
{
return null;
}
};
}
}
}
I'm trying to get the table with id table-matches available here. The problem is that table is loaded using ajax so I don't get the full html code when I download the page:
string url = "http://www.oddsportal.com/matches/soccer/20180701/";
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = client.GetAsync(url).Result)
{
using (HttpContent content = response.Content)
{
string result = content.ReadAsStringAsync().Result;
}
}
}
the html returned does not contains any table, so I tried to see if there is a problem of the library, infact I setted on Chrome (specifically on the Dev console F12) javascript off and same result on the browser.
Fox fix this problem I though to use a WebBrowser, in particular:
webBrowser.Navigate("oddsportal.com/matches/soccer/20140221/");
HtmlElementCollection elements = webBrowser.Document.GetElementsByTagName("table");
but I want ask if I can load also the full html doing asynchronus calls, someone has encountered a similar problem?
Could you please share a solution? Thanks.
The main issue with this page is that content inside table-matches is loaded via ajax. And neither HttpClient nor HtmlAgilityPack unable to wait for ajax to be executed. Therefore, you need different approach.
Approach #1 - Use any headless browser like PuppeteerSharp
using PuppeteerSharp;
using System;
using System.Threading.Tasks;
namespace PuppeteerSharpDemo
{
class Program
{
private static String url = "http://www.oddsportal.com/matches/soccer/20180701/";
static void Main(string[] args)
{
var htmlAsTask = LoadAndWaitForSelector(url, "#table-matches .table-main");
htmlAsTask.Wait();
Console.WriteLine(htmlAsTask.Result);
Console.ReadKey();
}
public static async Task<string> LoadAndWaitForSelector(String url, String selector)
{
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
ExecutablePath = #"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
});
using (Page page = await browser.NewPageAsync())
{
await page.GoToAsync(url);
await page.WaitForSelectorAsync(selector);
return await page.GetContentAsync();
}
}
}
}
In purpose of cleanness, I've posted output here here. And once you get html content you are able to parse it with HtmlAgilityPack.
Approach #2 - Use pure Selenium WebDriver. Can be launched in headless mode.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;
namespace SeleniumDemo
{
class Program
{
private static IWebDriver webDriver;
private static TimeSpan defaultWait = TimeSpan.FromSeconds(10);
private static String targetUrl = "http://www.oddsportal.com/matches/soccer/20180701/";
private static String driversDir = #"../../Drivers/";
static void Main(string[] args)
{
webDriver = new ChromeDriver(driversDir);
webDriver.Navigate().GoToUrl(targetUrl);
IWebElement table = webDriver.FindElement(By.Id("table-matches"));
var innerHtml = table.GetAttribute("innerHTML");
}
#region (!) I didn't even use this, but it can be useful (!)
public static IWebElement FindElement(By by)
{
try
{
WaitForAjax();
var wait = new WebDriverWait(webDriver, defaultWait);
return wait.Until(driver => driver.FindElement(by));
}
catch
{
return null;
}
}
public static void WaitForAjax()
{
var wait = new WebDriverWait(webDriver, defaultWait);
wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
}
#endregion
}
}
Approach #3 - Simulate ajax requests
If you analyse the page loading using Fiddler or browser's profiler (F12) you can see that all data is coming with these two requests:
So you can try to execute them directly using HttpClient. But in this case you may need to track authorization headers and maybe something else with each HTTP request.
I am trying to automate a reactjs application and the framework our project is using built on C# and protractor-net.
After any click or assert function I get the following error, but the defined action in the code executes successfully.
System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
----> OpenQA.Selenium.WebDriverTimeoutException : timeout
What is the cause of this error?
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Support.UI;
using Protractor;
using System;
using System.Collections.Generic;
public Class personalinformations
{
private NgWebDriver _ngdriver;
public PersonalInformations(IWebDriver driver)
{
_ngdriver = new NgWebDriver(driver);
PageFactory.InitElements(_ngdriver, this);
_ngdriver.IgnoreSynchronization = true;
}
[FindsBy(How = How.Id, Using = "btnSubmit")]
private IWebElement btnsave { get; set; }
public void saveSection()
{
WebDriverWait wait = new WebDriverWait(ngdriver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementIsVisible(By.XPath("//*#id='btnSubmit']"));
btnsave.Click();
}
}
Note: While using Thread.Sleep(1000) for wait sometimes the code works.Also I tried with Javascript to click the element the result is same.
Once you wait for the element through WebDriverWait and ExpectedConditions method ElementIsVisible as in the next step you are invoking Click() so instead of ElementIsVisible method you need to invoke ElementToBeClickable method as follows :
public void saveSection()
{
WebDriverWait wait = new WebDriverWait(ngdriver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//*#id='btnSubmit']"));
btnsave.Click();
}
this is a funny Exception: "System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.";
i met this for several times, but i searched this post "Exception has been thrown by the target of an invocation" error (mscorlib)
they said that you should check the root cause of this Exception behind.
so i added a
try {element.Click();} catch(Exception e){Console.WriteLine(e);}
then the exception seems escaped away...
I am using Wait.Until method to check if my page is already loaded or if it still loading .
This is how it looks like :
protected IWebElement FindElement(By by, int timeoutInSeconds)
{
StackTrace stackTrace = new StackTrace();
string callingMethod = stackTrace.GetFrame(1).GetMethod().Name;
string message = "Error finding element in method: " + callingMethod;
if (timeoutInSeconds > 0)
{
try
{
WebDriverWait wait = new WebDriverWait(chromeDriver, TimeSpan.FromSeconds(timeoutInSeconds));
wait.Until(ExpectedConditions.ElementIsVisible(by));
Thread.Sleep(800);
}
catch (Exception)
{
Assert(false, message);
throw new Exception(message);
}
}
return chromeDriver.FindElement(by);
}
But now we want to change our automation pages and start using FindBy foe every element , like this :
[FindsBy(How = How.Id, Using = "username")]
public IWebElement _logInUserName;
but wait.until needs "by" element .
I saw the abstract solution for this problem , but it is no good for my case .
can anyone know another solution that i can use ?
There is a ByFactory class in Selenium .NET solution. I took this implementation to achieve what you want:
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace SeleniumPlayground
{
public static class SeleniumHelper
{
public static FindsByAttribute GetFindsByAttributeFromField(Type pageObject, string iwebElementFieldName)
{
FieldInfo fi = pageObject.GetField(iwebElementFieldName);
FindsByAttribute attr = (FindsByAttribute)fi.GetCustomAttributes(typeof(FindsByAttribute), false).FirstOrDefault();
return attr;
}
public static By GeyByFromFindsBy(FindsByAttribute attribute)
{
var how = attribute.How;
var usingValue = attribute.Using;
switch (how)
{
case How.Id:
return By.Id(usingValue);
case How.Name:
return By.Name(usingValue);
case How.TagName:
return By.TagName(usingValue);
case How.ClassName:
return By.ClassName(usingValue);
case How.CssSelector:
return By.CssSelector(usingValue);
case How.LinkText:
return By.LinkText(usingValue);
case How.PartialLinkText:
return By.PartialLinkText(usingValue);
case How.XPath:
return By.XPath(usingValue);
case How.Custom:
if (attribute.CustomFinderType == null)
{
throw new ArgumentException("Cannot use How.Custom without supplying a custom finder type");
}
if (!attribute.CustomFinderType.IsSubclassOf(typeof(By)))
{
throw new ArgumentException("Custom finder type must be a descendent of the By class");
}
ConstructorInfo ctor = attribute.CustomFinderType.GetConstructor(new Type[] { typeof(string) });
if (ctor == null)
{
throw new ArgumentException("Custom finder type must expose a public constructor with a string argument");
}
By finder = ctor.Invoke(new object[] { usingValue }) as By;
return finder;
}
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Did not know how to construct How from how {0}, using {1}", how, usingValue));
}
}
And here's an example usage:
public class Page
{
private IWebDriver driver;
[FindsBy(How = How.Id, Using = "content")]
public IWebElement ele;
public Page(IWebDriver _driver)
{
this.driver = _driver;
}
}
Use as follows:
Page page = PageFactory.InitElements<Page>(driver);
FindsByAttribute findsBy = SeleniumHelper.GetFindsByAttributeFromField(typeof(Page), "ele");
By by = SeleniumHelper.GeyByFromFindsBy(findsBy);
I found a way to o this :)
public static IWebElement FindElement( IWebElement element, int timeoutInSeconds)
{
if (timeoutInSeconds > 0)
{
var wait = new WebDriverWait(chromeDriver, TimeSpan.FromSeconds(timeoutInSeconds));
return wait.Until(drv => element);
}
return element;
}
We faced the same issue when using selenium for testing. So we created a mini framework on top of selenium which keeps trying to do (whatever you are trying to do with selenium). Or otherwise you can provide a custom pre or post condition.
https://github.com/LiquidThinking/Xenon
It is very simple to setup and all information is available on github, plus it comes with Screen objects which can help to reuse your code.
For example
new XenonTest(new SeleniumXenonBrowser())
.GoToUrl("http://www.google.co.uk", a => a.PageContains("google") );
So in this example, we added a pre wait condition which says "before going to google.co.uk make sure that the current page contains "google" in page source. Which is obviously incorrect way to do it but it explains how to use pre or post wait conditions.
If you do not specify any wait condition then for some actions, there is a default wait action. for e.g. https://github.com/LiquidThinking/Xenon/blob/master/Xenon/BaseXenonTest.cs#L72
See how we check if a customPreWait is available for "Click" and if not then we added a custom pre-wait to check if that css selectors exists on the page before performing the "actual click action".
Hope it will help you, it is on nuget or otherwise just use the code which you want. it is under MIT license.
I have a webapp that will contain a class of "Loading" which, when fully loaded on the page, will contain a width property of 100% else it will contain nothing. I'm trying to perform a check on this style attribute but I keep getting a timeout. here is what I'm doing:
I'm calling code from within a helper / utility class as follows as this is something I will be using frequently in multiple classes:
Utility.WaitForStyle("Loading", Utility.driver);
In my helper / utility class I have the following code:
public static void WaitForStyle(string Class, IWebDriver driver)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
wait.Until<bool>((d) =>
{
try
{
IWebElement element = d.FindElement(By.ClassName(Class));
String elementresults = element.GetAttribute("style");
System.Diagnostics.Debug.WriteLine(elementresults);
return false;
}
catch (NoSuchElementException)
{
return true;
}
});
}
Note, The code above is currently just looking to check that it can get a handle on the class's style attribute but it's not getting to that point. I know the problem lies within the utility method as I can use the following code in individual classes:
IWebElement element = Utility.driver.FindElement(By.ClassName("Loading"));
String elementresults = element.GetAttribute("style");
System.Diagnostics.Debug.WriteLine(elementresults);
This will printout "width: 100%" as expected so I know that this block of code is actually working ok.
Does anyone know if I'm doing something silly in my utility method?
Here's my code to wait for an element attribute to have a specific value. It assumes the element passed to it has been verified to exist:
public bool WaitForAttribute(IWebDriver driver, IWebElement element, string attributeName, string attributeValue, int timeOut = 5)
{
// Build a function with a signature compatible with the WebDriverWait.Until method
Func<IWebDriver, bool> testCondition = (x) => element.GetAttribute(attributeName).Equals(attributeValue);
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut));
// Wait until either the test condition is true or timeout
try { wait.Until(testCondition); }
catch (WebDriverTimeoutException e) { }
// Return a value to indicate if our wait was successful
return testCondition.Invoke(null);
}
This is working for me, since more than 4 months.
public static WebDriverWait wait = new WebDriverWait(SeleniumInfo.Driver, TimeSpan.FromSeconds(20));
public static void WaitUntilAttributeValueEquals(this IWebElement webElement, String attributeName, String attributeValue)
{
wait.Until<IWebElement>((d) =>
{
if (webElement.GetAttribute(attributeName) == attributeValue)
{
return webElement;
}
return null;
});
}