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
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
}
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();
}
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.
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.
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();
}
});
}
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;
}
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);
}
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;
}
}
Related
I am using Selenium with C# to test a Web App. We have an implementation that when a section of a page is loading, we add a class to the div of the section and once the section has been loaded, the div class is removed from the div. For example:
Before
<div class="tabs-container spinner">
After:
<div class="tabs-container">
I am trying to add a Wait until the spinner is removed from the div class. I tried using
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.InvisibilityOfElementLocated(By.CssSelector("spinner")));
', however, this doesn't work.
Any idea how to handle this case?
To wait for the removal of the loader you have to induce WebDriverWait for the InvisibilityOfElementLocated() and you can use either of the following Locator Strategies:
CssSelector:
new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(SeleniumExtras.WaitHelpers.ExpectedConditions.InvisibilityOfElementLocated(By.CssSelector("div.tabs-container.spinner")));
XPath:
new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(SeleniumExtras.WaitHelpers.ExpectedConditions.InvisibilityOfElementLocated(By.XPath("//div[#class='tabs-container spinner']")));
Since you are using css selector.Try with .classname
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.InvisibilityOfElementLocated(By.CssSelector(".spinner")));
Or You can check .spinner class is not present using ElementIsVisible
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.CssSelector("div.tabs-container:not(.spinner)")));
You can write a custom function to wait for change in class attribute like
public static Func<IWebDriver, string> waitForChangeInAttribute(By locator, string attribute, string notValue) {
return (driver) => {
try {
var value = driver.FindElement(locator).GetAttribute(attribute);
return value == notValue ? null : value;
}
catch (NoSuchElementException) {
return null;
}
catch (StaleElementReferenceException) {
return null;
}
};
}
And you use/call like
string valueBefore = driver.FindElement(By.xpath("//div[contains(#class, 'tabs-')]")).GetAttribute(attribute);
wait.Until(waitForChangeInAttribute(By.xpath("//div[contains(#class, 'tabs-')]"), "class", valueBefore));
Note: Please take care of single and double quote as I am typing from Mobile keypad
You could create a quick hacky method with a while loop.
...
CheckLoaded("div[class*='spinner']", 5);
public void CheckLoaded(string css, int timeout)
{
timeout = timeout * 1000;
int count=0;
while(driver.FindElements(By.CssSelector(css) > 0 || count < timeout))
{
Thread.Sleep(1000);
}
}
I am new to Selenium and was using Telerik free testing framework before. Problem is I am not able to understand, how to use elements which are already identified with [FindsBy] to wait, check and click on.
ex:
[FindsBySequence]
[FindsBy(How = How.Id, Using = "container-dimpanel")]
[FindsBy(How = How.CssSelector , Using = ".btn.btn-primary.pull-right")]
public IWebElement UpdateButton { get; set; }
internal void ClickUpdateButton(TimeSpan timeout)
{
new WebDriverWait(_driver, timeout).
Until(ExpectedConditions.ElementIsVisible(By.CssSelector(id));
UpdateButton.Click();
}
I want my code to wait for update button to be visible and then click on it. But I want to just pass the UpdateButton element rather than using By selector.
not sure if UpdateButton.Enabled will wait until its visible.
There is an expected condition for visibility that accepts a WebElement:
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html#visibilityOf-org.openqa.selenium.WebElement-
Until also returns the element being waited for, so you can combine this into one line:
internal void ClickUpdateButton(TimeSpan timeout)
{
WebDriverWait wait = new WebDriverWait(_driver, timeout);
wait.Until(ExpectedConditions.visibilityOf(UpdateButton)).click();
}
However, in my frameworks I usually add a helper function that does this, as it get's used so much. You can also do similar things with wait until clickable, etc. and have methods that accept a WebElement or a By:
public WebElement waitThenClick(WebElement element)
{
WebDriverWait wait = new WebDriverWait(_driver, timeout);
return wait.Until(ExpectedConditions.visibilityOf(UpdateButton)).click();
}
The C# client doesn't have a builtin condition to check the visibility for a proxied WebElement.
Moreover the expected condition ExpectedConditions.ElementIsVisible checks that the element is displayed but doesn't check that the element is visible from a user perspective.
So the quickest and most reliable way is to retry the click in a waiter until success:
Click(UpdateButton, 5);
static void Click(IWebElement element, int timeout = 5) {
var wait = new DefaultWait<IWebElement>(element);
wait.IgnoreExceptionTypes(typeof(WebDriverException));
wait.PollingInterval = TimeSpan.FromMilliseconds(10);
wait.Timeout = TimeSpan.FromSeconds(timeout);
wait.Until<bool>(drv => {
element.Click();
return true;
});
}
Use this function I've written to test for an element, you can just pass in the name. It will return a bool and you could use a loop to wait for the elements to be present.
static public bool verify(string elementName)
{
try
{
bool isElementDisplayed = driver.FindElement(By.XPath(elementName)).Displayed;
return true;
}
catch
{
return false;
}
return false;
}
I goggled this issue but could not find a better answer, so... posting it here.
I click on a button in the browser, which opens up a form/div (which is generated dynamically). The form/div element does not exist until I press button.
Now, I am trying to check whether form/div element is existing or not. I tried with the below code. But it works when an element exists and throws exception (first method - timeout and for second, driver gets stopped) when the element does not exists.
Method 1:
ReadOnlyCollection<IWebElement> elements = Utility.Browser.FindElements(By.TagName("div")); // Utility.Browser is the browser instance.
var expElement = from e in elements
where e.GetAttribute("id").Contains("element id")
select e;
return expElement.Count() > 0;
and
Method 2:
string script = string.Format("return document.getElementById('{0}')", attValue);
IJavaScriptExecutor js = (IJavaScriptExecutor)Utility.Browser; // Utility.Browser is the browser instance.
var ele = js.ExecuteScript(script);
return ele != null;
Any help would be highly appreciated.
Thanks.
Look into WebDriverWait. You can define a wait function that will wait a specific amount of time to satisfy a specific condition. You can essentially say "wait for ten seconds for the element to appear". I'm on my phone and the exact syntax may be incorrect but it would look something like the following.
pulic bool ElementExist(IWebDriver driver)
{
var value = false;
var objWait = new WebDriverWait(driver, Timespan.FromMilliseconds(10000));
objWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
value = objWait.Until(b=>b.FindElements(By.TagName("div")).Count > 0);
return value;
}
You can specify which types of exceptions to ignore, such as ElementNotFound and StaleElement, and the function will continue to wait if those occur. You can also define a function and pass that as a parameter to the .Until function. My skills in lamda expressions and inline function definitions are lacking, otherwise I would give a better example but that is definitely the most customizable approach.
similarly to the other two answers already here, I fashion my test using an extension method along the lines of:
public static bool ElementExists(this IWebDriver driver, By condition, TimeSpan? timeSpan)
{
bool isElementPresent = false;
if (timeSpan == null)
{
// default to 15 seconds if timespan parameter is not passed in
timeSpan = TimeSpan.FromMilliseconds(15000);
}
var driverWait = new WebDriverWait(driver, (TimeSpan)timeSpan);
driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
isElementPresent = driverWait.Until(x => x.FindElements(condition).Any());
return isElementPresent;
}
I then use this in code as such:
var isElementPresent = _driver.ElementExists(By.ClassName("register"), TimeSpan.FromSeconds(90.00));
if (isElementPresent)
{
// do required processing...
}
Hope this helps
[edit] - you could of course refactor the extension method to return the required element, with a default of null if you wanted to do everything in a single action.
public static IWebElement FindElementAfterWait(this IWebDriver driver, By condition)
{
bool isElementPresent = false;
IWebElement singleElement = null;
var driverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(90));
driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
isElementPresent = driverWait.Until(x => x.FindElement(condition) != null);
if (isElementPresent)
{
singleElement = driver.FindElement(condition);
}
return singleElement;
}
usage:
_driver.FindElementAfterWait(By.ClassName("register"));
also:
public static IWebElement FindElementAfterWait(this IWebDriver driver, Func<IWebDriver, IWebElement> condition)
{
IWebElement singleElement = null;
var driverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(90));
driverWait.IgnoreExceptionTypes(typeof(WebDriverTimeoutException));
singleElement = driverWait.Until(condition);
return singleElement;
}
usage:
_driver.FindElementAfterWait(ExpectedConditions.ElementIsVisible(By.Id("firstName")))
enjoy...
The following function helps me to test the existence of an element on a page in C# Selenium code:
public static bool IsElementPresent(this IWebDriver driver, By by)
{
return driver.FindElements(by).Count > 0;
}
Please let me know if it helps you!
Following method is the one that I always use, and trust me really does what it says.
It will return true if the specified element is displayed else it will return false.
You can use it like : IsElementDisplayedByXpathVariableWait("Xpath_Of_The_Element",5);
5 is the number of times it will check if the element is displayed with a pause of 1 sec after every check.
public static bool IsElementDisplayedByXpathVariableWait(string xpath, int iterations)
{
bool returnVal = false;
int tracker = 0;
while (tracker < iterations)
{
try
{
tracker++;
IWebElement pageObject = _driver.FindElement(By.XPath(xpath));
if (pageObject.Displayed)
{
returnVal = true;
break;
}
}
catch (Exception e)
{
Wait(1000);
continue;
}
}
return returnVal;
}
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;
});
}
In a normal loop you can break out of a loop using break. Can the same be done using an anonymous delegate?
Example
inputString and result are both declared outside the delegate.
blackList.ForEach(new Action<string>(
delegate(string item)
{
if(inputString.Contains(item)==true)
{
result = true;
// I want to break here
}
}
));
Edit:
Thanks for the replies, I'm actually reading your book at the minute John :) Just for the record i hit this issue and switched back to a normal foreach loop but I posted this question to see if i missed something.
As others have posted, you can't exit the loop in ForEach.
Are you able to use LINQ? If so, you could easily combine TakeWhile and a custom ForEach extension method (which just about every project seems to have these days).
In your example, however, List<T>.FindIndex would be the best alternative - but if you're not actually doing that, please post an example of what you really want to do.
There is no loop that one has access to, from which to break. And each call to the (anonymous) delegate is a new function call so local variables will not help. But since C# gives you a closure, you can set a flag and then do nothing in further calls:
bool stop = false;
myList.ForEach((a) => {
if (stop) {
return;
} else if (a.SomeCondition()) {
stop = true;
}
});
(This needs to be tested to check if correct reference semantics for closure is generated.)
A more advanced approach would be to create your own extension method that allowed the delegate to return false to stop the loop:
static class MyExtensions {
static void ForEachStoppable<T>(this IEnumerable<T> input, Func<T, bool> action) {
foreach (T t in input) {
if (!action(t)) {
break;
}
}
}
}
Do you have LINQ available to you? Your logic seems similar to Any:
bool any = blackList.Any(s=>inputString.Contains(s));
which is the same as:
bool any = blackList.Any(inputString.Contains);
If you don't have LINQ, then this is still the same as:
bool any = blackList.Find(inputString.Contains) != null;
If you want to run additional logic, there are things you can do (with LINQ) with TakeWhile etc
I don't think there's an elegant way to do it when using the ForEach method. A hacky solution is to throw an exception.
What's preventing you from doing an old fashioned foreach?
foreach (string item in blackList)
{
if (!inputString.Contains(item)) continue;
result = true;
break;
}
If you want a loop, use a loop.
Action allows for no return value, so there's no way the ForEach function could possibly know that you want to break, short of throwing an exception. Using an exception here is overkill.
The only way to "exit" the loop is to throw an exception. There is no "break" style way of exiting the .ForEach method like you would a normal foreach loop.
The ForEach method is not mean to do this. If you want to know if a collection contains an item you should use the Contains method. And if you want to perform a check on all items in a collection you should try the Any extention method.
bool #break = false;
blackList.ForEach(item =>
{
if(!#break && inputString.Contains(item))
{ #break = true;
result = true;
}
if (#break) return;
/* ... */
});
Note that the above will still iterate through each item but return immediately. Of course, this way is probably not as good as a normal foreach.
class Program
{
static void Main(string[] args)
{
List<string> blackList = new List<string>(new[] { "jaime", "jhon", "febres", "velez" });
string inputString = "febres";
bool result = false;
blackList.ForEach((item) =>
{
Console.WriteLine("Executing");
if (inputString.Contains(item))
{
result = true;
Console.WriteLine("Founded!");
}
},
() => result);
Console.WriteLine(result);
Console.ReadLine();
}
}
public static class MyExtensions
{
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action, Func<bool> breakOn)
{
foreach (var item in enumerable)
{
action(item);
if (breakOn())
{
break;
}
}
}
}
Would this work for you:
bool result = null != blackList.Find( item => inputString.Contains(item)) );
blackList.ForEach(new Action<string>(
delegate(string item)
{
if(inputString.Contains(item)==true)
{
result = true;
// I want to break here
return;
}
}
));
if you realy want to exist a loop foreach in a list you could use the exception like this code:
public class ExitMyForEachListException : Exception
{
public ExitMyForEachListException(string message)
: base(message)
{
}
}
class Program
{
static void Main(string[] args)
{
List<string> str = new List<string>() { "Name1", "name2", "name3", "name4", "name5", "name6", "name7" };
try
{
str.ForEach(z =>
{
if (z.EndsWith("6"))
throw new ExitMyForEachListException("I get Out because I found name number 6!");
System.Console.WriteLine(z);
});
}
catch (ExitMyForEachListException ex)
{
System.Console.WriteLine(ex.Message);
}
System.Console.Read();
}
}
hope this help to get other point of view.