I am trying to create a test script using selenium that might interact with an element but if the element is not there it will not. I need to add that the element might take some time to appear. The issue is that if I use FindElement I get an exception. If I use FindElements it take too long. So, I tried to used "until" function which work well to wait until the element to appear... but if it doesn't appear it throws an exception I want to avoid that.
I know I can use a try catch.But, is there any better way to do this?
I curently have this:
IWebElement button;
try{
string x = "search query";
button = this.WaitDriver.Until(d => d.FindElement(By.XPath(x)));
}catch{
button = null;
}
If you want to test for existence without a try-catch then you would have to use .FindElements(). It shouldn't take any longer than .FindElement() using the same wait but the wait will time out if the element doesn't exist... which is a pain. One way to get around this is to do something like the below. It's basically what you had with a few tweaks.
public bool ElementExists(By locator)
{
try
{
new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(ExpectedConditions.ElementExists(locator));
return true;
}
catch (Exception e) when (e is NoSuchElementException || e is WebDriverTimeoutException)
{
return false;
}
}
I prefer checking for existence rather returning a null because then you have to check for null also. By putting this in a function, you can call it whenever and your tests stay cleaner. You would use it like
By locator = By.Id("someId");
if (ElementExists(locator))
{
IWebElement e = driver.FindElement(locator);
// do something with e
}
Related
I'm very new to Selenium so I'm looking for some insight on how to achieve this.
Essentially, I want my driver to wait for one of multiple elements to be visible. This is because the website can produce mixed results when using automation, so I want to treat this like it's "error handling" (if that makes sense).
Basically, if element a is visible first, do this. However, if element b is visible first, do that.
How would I be able to achieve this using the Selenium webdriver for C#? I read that combining the elements' XPath value by using "|" worked, however when I tried it with my application, I had no luck.
Any answers are greatly appreciated! Thank you in advance!
To me this is kind of tough because your elements could be displayed within a split second of each other. Wonder if you could put this in a case and break when you get one?
So what I was thinking was seeing if the element was present first.
public static bool IsElementPresent_byXpath(string xpath)
{
bool result;
try { result = Driver.FindElement(By.XPath(xpath)).Displayed; }
catch (NoSuchElementException) { return false; }
catch (StaleElementReferenceException) { return false; }
return result;
}
So if you do it this way, then you can write something like the below but my concern is that if the page is not changing, ele1, ele2 and ele3 could be invoked. So this code would need to be changed.
bool ele1 = Browser.IsElementPresent_byXpath("//[#id='1']");
bool ele2 = Browser.IsElementPresent_byXpath("//[#id='2']");
bool ele3 = Browser.IsElementPresent_byXpath("//[#id='3']");
if (ele1)
{
//do this
}
else if (ele2 || ele1 == false)
{
//do this
}
else if (ele3 || ele1 == false || ele2 == false)
{
//do this
}
I would go with Wait.Until, so that I can combine the benefits of waiting with set timeout for an event, and returning me matching locator, so I can base my logic on the result.
This is java code, but C# has equivalents.
Assuming I am waiting for either firstBy, secondBy or thirdBy:
By locatorOfElementThatIsPresent = new WebDriverWait(webDriver, timeoutInSeconds).<By>until(driver -> {
// findElements does not throw when element not found, so I skip try-catch
if (driver.findElements(firstBy).size() > 0) return firstBy;
if (driver.findElements(secondBy).size() > 0) return secondBy;
if (driver.findElemetns(thirdBy).size() > 0) return thirdBy;
return null; // neither found, so the method will be retried until timeout
});
I have the following elements identified in a page object..
public WindowsElement usernameField => _session.FindElementByAccessibilityId("UserName");
public WindowsElement passwordField => _session.FindElementByAccessibilityId("Password");
public WindowsElement menuButton => _session.FindElementByXPath("//Button[contains(#Name, 'Menu')]");
I have a test with the following steps..
WaitForObject(usernameField)
usernameField.SendKeys("...")
WaitForObject(passwordField)
passwordField.SendKeys("...")
ClickButton("Sign In");
WaitForObject(menuButton);
menuButton.Click();
And below, here is my WaitForObject method..
// Wait for an Object to be accessible
public void WaitForObject(WindowsElement element)
{
var wait = new DefaultWait<WindowsDriver<WindowsElement>>(_session)
{
Timeout = TimeSpan.FromSeconds(10),
PollingInterval = TimeSpan.FromSeconds(1)
};
wait.IgnoreExceptionTypes(typeof(WebDriverException));
wait.IgnoreExceptionTypes(typeof(InvalidOperationException));
wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException));
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
wait.IgnoreExceptionTypes(typeof(NotFoundException));
WindowsElement waitElement = null;
wait.Until(driver =>
{
waitElement = element;
return waitElement != null && waitElement.Enabled && waitElement.Displayed;
});
}
The WaitForObject method works great for the usernameField & passwordField checks, but for some reason it fails immediately when passing in the menuButton. I know it was checking properly for usernameField & passwordField because I included some Console.WriteLines() to print out whenever it would detect one of those exceptions. As soon as it gets to the menuButton, nothing is logged it just fails immediately with a WebDriverException
OpenQA.Selenium.WebDriverException : An element could not be located on the page using the given search parameters.
Why would it not act the same for the menuButton? I have tried other approaches using while loops catching general Exceptions, but still it fails immediately when it gets to this element with a WebDriverException.
If I use a Thread.Sleep(10000) before trying to check for the element, it works fine and performs the click..
I am using WinAppDriver / Appium libraries.
Oh wait, sorry, I looked at your code more closely. Basically what's happening is because the method parameter is asking for the type itself, when C# goes to hand in the element to WaitForObject, it tries to evaluate the "WindowsElement menuButton" expression when it's handed to WaitForObject. By changing the WaitForObject method to accept a delegate, you'll defer that evaluation until you're inside the wait.
You'll need to change your WaitForObject to be something like:
public void WaitForObject(Func<WindowsElement> element)
// Unchanged code here
wait.Until(driver =>
{
waitElement = element();
return waitElement != null && waitElement.Enabled && waitElement.Displayed;
});
THEN call it like:
WaitForObject(() => menuButton);
menuButton.Click();
you can try using regex instead of button use '*'
as inspect.exe is not defining the tag i.e. button.
else go With finding the element with Name locator.
the html
<span id="banner">Rolling in 10.00...</span>
my code
public void waitroller()
{
Console.WriteLine("waiting roller");
WebDriverWait wait = new WebDriverWait(PropertiesCollection.driver, TimeSpan.FromDays(2));
wait.Until<IWebElement>((d) =>
{
IWebElement element = PropertiesCollection.driver.FindElement(By.Id("banner"));
if (element.Text == "Rolling in 20.00...")
{
return element;
}
return null;
});
}
OR
public void waitroller()
{
new WebDriverWait(PropertiesCollection.driver, TimeSpan.FromMinutes(1.5)).Until(ExpectedConditions.TextToBePresentInElementLocated(By.Id("banner") , "Rolling in 20.00..."));
}
I'm stuck with this innertext because the innertext in the html is always changing and i want to make a webdriverwait that waits until the innertext in the span banner is "Rolling in 20.00" . Is there any way to make custom expected conditions because I am not understand custom expected condition.
what i need is something like this
new WebDriverWait(PropertiesCollection.driver, TimeSpan.FromMinutes(1.5)).Until(ExpectedConditions.TextToBePresentInElement(PropertiesCollection.driver.FindElement(By.Id("banner"), "Rolling in 20.00...");
but this is not work because i think the id="banner" is innerText
there is one way that work but can't work for long because i think it is the wrong way of doing it . i loop the element banner many times and check if the innerText is equals to "Rolling in 20.00..." but have error also
public void waitroller()
{
Console.WriteLine("waiting roller");
WebDriverWait wait = new WebDriverWait(PropertiesCollection.driver, TimeSpan.FromDays(2));
IWebElement bannerElement = PropertiesCollection.driver.FindElement(By.Id("banner"));
wait.Until((d) =>{ return bannerElement.Text.Contains("Rolling in 20.00"); });
}
I don't know what you mean by "innertext", but with Selenium's WebDriverWait you can wait for whatever you want. You don't have to use the ExpectedConditions, they're only for programmers' comfort.
Example:
new WebDriverWait(...).Until(driver => driver.FindElement(...).Text.Equals(...));
On the other hand, your problem might be a completely different one. Is your "roller" a countdown where the text changes very fast? In this case you might never reach the expected condition. I think Selenium checks the expected conditions every 500 milliseconds, so it doesn't even notice when "Rolling in 20.00" is displayed for just let's say 50 millisecods and then has already changed to "Rolling in 19.95" when Selenium checks for the next time.
I'm using Selenium Webdriver with C# and I'm wondering if there's a way to override the FindElement method? What I'd like to do - if possible - is to add an extra parameter and code to the method that would force it to wait for the element to be visible before proceeding.
For example, it would then be something like this:
Driver.FindElement(By.Id("orion.dialog.box.ok"), 60).Click();
This would wait up to 60 seconds for the element to appear and be available to click.
Any ideas how to do this?
Thanks,
John
You could use ImplicitWait for this. You can create a new user defined function to accept the By object and the timeout seconds and have the function return the IWebElement, something like below:
IWebElement elem = MyOwnGetElement(By.id("test"),60);
The above function might have the below code, where time_out_sec is the function parameter.
WebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(time_out_sec));
driver.Url = "http://somedomain/url_that_delays_loading";
IWebElement myDynamicElement = driver.FindElement(By.Id("someDynamicElement"));
I would suggest you add it as an extension method. Pseudo code:
public static IWebElement WaitForAndFindElement(this IWebDriver driver, By by)
{
// do a wait
// something like...
WebDriverWait wait = new WebDriverWait(TimeSpan.FromSeconds(60)); // hard code it here if you want to avoid each calling method passing in a value
return wait.Until(webDriver =>
{
if (webDriver.FindElement(by).Displayed)
{
return webDriver.FindElement(by);
}
return null; // returning null with force the wait class to iterate around again.
});
}
Just to state, an implicit wait is going to be part one of your solution. It will cause Selenium to wait up to 60 seconds for an element to be present in DOM but being visible is something entirely different.
.Displayed will handle that, and it must be handled within a WebDriverWait.
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 use it like this:
IWebElement x = driver.FindElement(By.Id("username"));
x.SafeClick();
Guys, I have problem with waitForPageToLoad condtion. I'm using C# with Nunit, and I don't know exactly what I should use instead of 'waitForPageToLoad'/'waitForElementPresent'. Maybe someone knows how I can use 'waitForPageToLoad'/'waitForElementPresent' in Selenium 2?
I use FindElement() for something on the new page. Wrap in in a loop that will keep trying until the element is found, with a maximum wait.
// pseudocode WebElement wait_for_element(By locator)
while (!timeout) {
try {
return driver.FindElement(locator);
} catch (Exception) {
Thread.sleep(1000);
}
// check for timeout here
}
throw new NoSuchElementException("Timeout waiting for "+locator);
ISelenium selenium = new DefaultSelenium( "localhost", 4444, "*chrome", "
http://localhost/UrlOfAppToTest/" );
selenium.WaitForPageToLoad( "30000" );
Is that what you're looking for?
You could use a explicit wait method to wait for your action/page to load.
With that you can specify a condition to be met, and when it is met your test will continue.
See link for more details:
Selenium HQ, explicit wait