Reputation: 521
In my C# Windows Forms application using Firefox Selenium WebDriver I need to check if an element exists and if it doesn't, click a different one. If there is a video, after it is watched it becomes W_VIEWED
:
driver.FindElement(By.XPath("//div[@class='video']/a")).Click();
else
{
driver.FindElement(By.XPath("//div[@class='W_VIEWED']/a")).Click();
}
Error 3 Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement 242
Upvotes: 34
Views: 119960
Reputation: 21
Best practice (to avoid try/catch) would be to use the FindElements().Count
function (note the 's' of elements).
If the element doesn't exist, the count will be 0
, otherwise the element exist
if(driver.FindElements(By.XPath("//div[@class='video']/a")).Count > 0) {...}
The issue with using the FindElement().Displayed
bool is that it will throw an exception if the element doesn't exist.
If you wish to have similar bool element, you can use a ternary operator which will handle both existence cases :
bool Displayed = driver.FindElements(By.XPath("//div[@class='video']/a")).Count > 0 ? true : false;
Upvotes: 0
Reputation: 2946
If you are using ImplicitWait
and want to see if an element is present by using FindElement
without waiting, try this code:
ElementExists(By.Id(id));
static public bool ElementExists(By method)
{
var oldTime = _driver.Manage().Timeouts().ImplicitWait;
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(1);
try
{
bool isElementDisplayed = _driver.FindElement(method).Displayed;
_driver.Manage().Timeouts().ImplicitWait = oldTime;
return true;
}
catch
{
_driver.Manage().Timeouts().ImplicitWait = oldTime;
return false;
}
}
Upvotes: 0
Reputation: 478
This is what I use. The "verify" method will return a true or false based on if the element exists. The method named "testfunc" is where you enter the element name. In this example I am looking to see if "English" is displayed on the page.
Also, I notice in the comments in previous posts, people are saying they have to wait 10 seconds or more for the catch to work. Try remove the explicit wait in your code for the catch to work immediately.
static public bool verify(string elementName)
{
try
{
bool isElementDisplayed = driver.FindElement(By.XPath(elementName)).Displayed;
return true;
}
catch
{
return false;
}
return false;
}
static void testfunc()
{
bool test = verify("//option[contains(.,'English')]");
Console.WriteLine(test);
}
Upvotes: 0
Reputation: 211
Dominic Giallombardo's answer worked for me. On Ajax-based information which loads on background, it is a required loop to wait for the element to appear.
So if you want to wait and do an action when the element appear it is possible with a label and a go to label + else condition. Here is the modified code which will wait for the element to appear through a loop:
checksomeelement:
List<IWebElement> elementList = new List<IWebElement>();
elementList.AddRange(driver.FindElements(By.XPath("//div[@class='video']/a")));
if (elementList.Count > 0)
{
elementList[0].Click();
}
else
{
System.Threading.Thread.Sleep(2000);
goto checksomeelement;
}
Upvotes: 0
Reputation: 4119
This method will allow you to wait for an element to exist. This is especially important in front end SPA frameworks that conditionally create elements, like Vue.js. You can tweak your retry count based on the performance of your application. In any case, it will wait for your ELEMENT_FIND_WAIT_TIME * ELEMENT_FIND_WAIT_RETRY_COUNT
milliseconds before failing completely. This solved the problem we were having.
protected Func<IWebElement> GetLazyElement(By by, int retryCount=0)
{
if (retryCount >= ELEMENT_FIND_WAIT_RETRY_COUNT)
{
throw new Exception("Wait timeout for element to show up" + by.ToString());
}
return new Func<IWebElement>(() => {
try
{
Debug.WriteLine("Finding element " + by.ToString());
var element = _webDriver.FindElement(by);
return element;
}
catch (Exception)
{
Debug.WriteLine($"Failed to find element: {by} (Waiting {ELEMENT_FIND_WAIT_TIME}ms)");
Thread.Sleep(ELEMENT_FIND_WAIT_TIME);
var lazyFunc = GetLazyElement(by, retryCount++);
return lazyFunc();
}
});
}
Upvotes: 0
Reputation: 1407
I used the accepted answer's solution for some time, but I needed a faster way to check, without waiting for the timeout period every time a check failed. So I made some extension functions that work on IWebElement and IWebDriver that check for the existence of a tag or class.
public static class ExtensionMethods {
public static bool ContainsTag(this IWebElement element, string tagName)
{
string elementText = element.GetAttribute("innerHTML");
return CheckStringForTag(elementText, tagName);
}
public static bool ContainsClass(this IWebElement element, string className)
{
string elementText = element.GetAttribute("innerHTML");
return CheckStringForClass(elementText, className);
}
public static bool ContainsTag(this IWebDriver driver, string tagName)
{
return CheckStringForTag(driver.PageSource, tagName);
}
public static bool ContainsClass(this IWebDriver driver, string className)
{
return CheckStringForClass(driver.PageSource, className);
}
private static bool CheckStringForTag(string text, string tagName)
{
if (!string.IsNullOrWhiteSpace(text))
{
return text.Contains("<" + tagName + ">") || text.Contains("</" + tagName + ">") || text.Contains("<" + tagName + " ");
}
return false;
}
private static bool CheckStringForClass(string text, string className)
{
if (!string.IsNullOrWhiteSpace(text))
{
string pattern = string.Format(".*class[\\s]?=[\\s]?.*[\\s'\"]{0}[\\s'\"].*.*", className);
Match m = Regex.Match(text, className, RegexOptions.IgnoreCase);
return m.Success;
}
return false;
}
public static string InnerHTML(this IWebElement element)
{
return element.GetAttribute("innerHTML");
}
}
Note: This is similar to, but expands on Dominic Giallombardo's answer.
Upvotes: 0
Reputation: 955
So I recently figured out another way, which is much faster. If your element has a unique ID or some attribute that exists nowhere else on the page, you can check the PageSource.
driver.PageSource.Contains("UniqueID");
It checks the page to see if the ID or other unique text exists. This happens almost instantaneously, as opposed to using a Try/Catch statement, which takes ~20 seconds. FindElements takes a long time to run too.
Upvotes: 13
Reputation: 955
You can use FindElements with an "s" to determine if it exists, since FindElement results in an Exception. If FindElements does not return an element then it returns an empty list.
List<IWebElement> elementList = new List<IWebElement>();
elementList.AddRange(driver.FindElements(By.XPath("//input[@att='something']")));
if(elementList.Count > 0)
{
//If the count is greater than 0 your element exists.
elementList[0].Click();
}
Upvotes: 33
Reputation: 9841
You can check if an element exits or not by using
bool isElementDisplayed = driver.findElement(By.xpath("element")).isDisplayed()
Remember, findElement
throws an exception if it doesn't find an element, so you need to properly handle it.
In one of my applications, I handled an exception by checking the element in a separate function:
private bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
Call function:
if (IsElementPresent(By.Id("element name")))
{
// Do if exists
}
else
{
// Do if does not exists
}
Upvotes: 53