I have been working on making my Selenium Framework a Page Factory, however i am struggling to get the Wait.Until commands working in my Extension Class.
public static void Wait(this IWebElement element, IWebDriver driver, float TimeOut)
{
WebDriverWait Wait = new WebDriverWait(driver, TimeSpan.FromSeconds(TimeOut));
return Wait.Until(ExpectedConditions.ElementIsVisible(element));
}
If I use the above code I get the error
Cannot Convert from OpenQA.Selenium.IWebElement to Open.Qa.Selenium.By
Any suggestions how can I amend the code above to make it work in the By model I am using?
There is no ExpectedConditions.ElementIsVisible(IWebElement). Unfortunately, you can only use ElementIsVisible with By objects.
If appropriate, you could substitute with ExpectedConditions.ElementToBeClickable(IWebElement), which is a slightly different case that also checks that the element is enabled in addition to being visible. But this may satisfy your requirement.
Alternatively, you could just call element.Displayed in a custom WebDriverWait, making sure to ignore or catch the NoElementException
Here is an old implementation of this I've used and changed for your case, there may be a cleaner way to do it now:
new WebDriverWait(driver, TimeSpan.FromSeconds(TimeOut))
{
Message = "Element was not displayed within timeout of " + TimeOut + " seconds"
}.Until(d =>
{
try
{
return element.Displayed;
}
catch(NoSuchElementException)
{
return false;
}
}
A quick explanation for the code above... It will try to execute element.Displayed over and over until it returns true. When the element does not exist, it will throw a NoSuchElementException which will return false so the WebDriverWait will continue to execute until both the element exists, and element.Displayed returns true, or the TimeOut is reached.
Replace
return Wait.Until(ExpectedConditions.ElementIsVisible(element));
with the below line of code and check, as this worked for me
wait.Until(ExpectedConditions.ElementIsVisible(By.Id("ctlLogin_UserName")));
wherein "ctlLogin_UserName" is the ID of your web element.
Related
I have a blazor application running fine and want to have some behavior test with selenium. The test does now currently the following:
goes to a page (directly using an URL, no page loaded before)
tries to click on a button
The first point does work, but the second has an issue. If I use the wait.until the button is available, then I receive back an early version of the button, which then redrawn and updated in the DOM later. This will give me the "stale element reference: element is not attached to the page document" error.
Here is the code:
var xPath = By.XPath($".//tr//td[normalize-space()=\"{name}\"]/ancestor-or-self::tr//button");
var button = _wait.Until(ExpectedConditions.ElementToBeClickable(xPath));
Thread.Sleep(1000);
button = _chromeDriver.FindElement(xPath);
button.Click();
the _wait.until will return an item that will be stale, while the next FindElement will return a valid, but only after ~1 sec sleep. If I don't have sleep there, it will return the same as the other line.
The final question: How can I ensure in the _wait.until line, that my returned element is the final one to avoid using Thread.Sleep?
There is no gentle solution for such issues.
The only solution I know for this is to make a loop:
Wait for element clickability and click it.
In case exception is thrown wait for the clickability again.
If click succed - break the loop and continue to outer code / return True.
Something like this:
for(i=0; i<6; i++){
try: {
wait.Until(ExpectedConditions.ElementToBeClickable(xPath)).Click();
break;
}
catch(Exception e){
Thread.Sleep(100);
}
}
#golddragon007, you can wait for the element to become stale.
_wait.Until(ExpectedConditions.StalenessOf(_chromeDriver.FindElement(xPath)))
you can check the following link for more details:
https://www.selenium.dev/selenium/docs/api/dotnet/html/M_OpenQA_Selenium_Support_UI_ExpectedConditions_StalenessOf.htm
Selenium Wait.Until conditions are not 100% reliable. I was in shock when I figure×’ this myself, but yes, after the condition has finished you sometimes have to wait or try clicking or hovering in a loop.
I wish to suggest 2 options here:
Wait for the element to be both Clickable and Enabled:
getWait().until((ExpectedCondition) driver -> element.isEnabled());
Use a much better UI tool for testing like Playwright. It has a much sophisticated waiting system conditions and is also faster and very reliable.
Prophet's answer is a good approach, so have a look at that. Sometimes you just need to try something more than one time. This is an alternative that is easier to reuse and more flexible.
All of the methods on the WebDriverWait class expect a lambda express to return a value. Sometimes I need to call a method with a void return type, so I write an extension method on WebDriverWait to support this:
public static class WebDriverWaitExtensions
{
public static void Until(this WebDriverWait wait, Action<IWebDriver> action)
{
wait.Until(driver =>
{
action(driver);
return true;
});
}
}
This is much more flexible and easier to call with your code. This will attempt to click the element until it succeeds. A word of caution, however. The StaleElementException will cancel a wait operation immediately. Be sure to ignore this kind of exception.
var xPath = By.XPath($".//tr//td[normalize-space()=\"{name}\"]/ancestor-or-self::tr//button");
_wait.IgnoredExceptionTypes(typeof(StaleElementException));
_wait.Until(driver => driver.FindElement(xpath).Click());
This should work with any web driver method that has a void return type.
It seems another solution is this:
_wait.Until(driver => (bool)((IJavaScriptExecutor)driver).ExecuteScript("return typeof Blazor.reconnect != 'undefined'"));
as that variable only loaded when the page is fully loaded. And after that it's possible to do a simple find without an issue:
var button = _chromeDriver.FindElement(xPath);
Implement polly as your resilience framework for Selenium methods you create an example you can see below:
public static IWebElement Click(string xPath, int retries = 15,
int retryInterval = 1)
{
var element = Policy.HandleResult<IWebElement>(result => result == null)
.WaitAndRetry(retries, interval => TimeSpan.FromSeconds(retryInterval))
.Execute(() =>
{
var element = Searchers.FindWebElementByXPath(xPath);
if (element != null)
{
_logger.Info("Clicked Element: " + xPath + " (" + element.Text + ")");
try
{
_driver.ExecuteScript("arguments[0].click();", element);
return element;
}
catch (Exception e)
{
if (e is ElementClickInterceptedException or StaleElementReferenceException)
return null;
}
}
return null;
});
if (element != null) return element;
_logger.Info("Failed to click Element: " + xPath + "");
throw new Exception(" Failed to use Javascript to click element with XPath: " + xPath + "");
}
By leveraging the power of polly, I can guarantee that all of my events will go through.
Do not use the base Selenium methods to wait for certain properties to change/be a certain value, they are not very reliable.
I want to create a generic method to be used across the test suite. This method should take the web element as a parameter. Then the method should return true or false based on the visibility of it during the configured time out.
public bool WaitForElementVisibility(IWebDriver driver, IWebElement element)
{
try
{
//code to wait until element is displayed similar to this. But instead of passing By.Id(login) pass the element
new WebDriverWait(driver,TimeSpan.FromSeconds(timeOut)).Until(ExpectedConditions.ElementExists((By.Id(login))));
return true;
}
catch(Exception e)
{
return false;
}
}
I'm creating a framework in selenium, C#.
You can see my progress in this https://umangamadawala.blogspot.com/.
Thanks in advance.
I think that you are searching something like this code:
You can pass your By.Id("Login") as a By element without any problem, so the call to this function will be something like:
WaitUntilElementVisible(By.Id("Login")); and if you want to change in this call the timeout time you will call like this: WaitUntilElementVisible(By.Id("Login"),45);
You cam put there your Try and catch logic. I use ElementIsVIsible but you can use ElementExist only changing the call.
protected virtual IWebElement WaitUntilElementVisible(By elementLocator, int timeout = 30)
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeout));
return wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(elementLocator));
}
I am writing a test where I want to verify that an element is NOT present on a page (displayed or otherwise). I've read in various articles (like this one) how to do the element detection with a list that's empty or not. That works just fine for the opposite test that verifies the elements ARE present. However, when the element is not present, I am consistently getting a WebDriverException timeout after 60 secs of spinning: See screenshot here
The element detection function is as such:
public bool isButtonPresent(string buttonType)
{
switch (buttonType)
{
case "Button 1":
return !(Driver.FindElements(By.Id("Button 1 ID Here")).Count == 0);
case "Button 2":
return !(Driver.FindElements(By.Id("Button 2 ID Here")).Count == 0);
case "Button 3":
return !(Driver.FindElements(By.Id("Button 3 ID Here")).Count == 0);
}
return false;
}
Thank you for your time!
Here are the options:
Option 1:
// Specify the amount of time the driver should wait when searching for an element if it is not immediately present. Specify this time to 0 to move ahead if element is not found.
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0));
// InvisibilityOfElementLocated will check that an element is either invisible or not present on the DOM.
wait.Until(ExpectedConditions.InvisibilityOfElementLocated(byLocator));
With this approach, if your next action is to click on any element, sometimes you will get an error (org.openqa.selenium.WebDriverException: Element is not clickable at point (411, 675)) with Chrome browser. it works fine with Firefox.
Option 2:
// Set the implicit timeout to 0 as we did in option 1
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0));
WebDriverWait wait = new WebDriverWait(Driver.Browser, new TimeSpan(0, 0, 15));
try
{
wait.Until(FindElement(By));
}
catch(WebDriverException ex) // Catch the WebDriverException here
{
return;
}
Here we are making implicit wait to 0 and finding element. if element is is not present it will try for next 15 seconds (you can change this number by your convenience). if there is time out, complete the function.
Option 3:
Sudeepthi has already suggested it.
Drivers._driverInstance.FindElement(by).Displayed;
Will something like this work?
public static bool IsElementPresent(By by)
{
try
{
bool b = Drivers._driverInstance.FindElement(by).Displayed;
return b;
}
catch
{
return false;
}
}
Forget the ExpectedConditions and the WebDriverWait -- both don't work really well.
If you have a ImplicitWait, the WebDriverWait is completely ignored. So you can not set the default timeout over ImplicitWait to 10 seconds and then create a WebDriverWait instance with 1 second only to check, if a element is present
WebDriverWait ignores the IgnoreExceptionTypes = WebDriverTimeoutException, so you need allways a try catch
With the following approach you have a simple and comfortable solution that works with a minimum of code:
using System.Linq;
#Decrease the timeout to 1 second
Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1);
#FindElements get an array and Linq take the first or null
var result = Driver.FindElements(By.TagName("h7")).FirstOrDefault();
#Increase the timeout to 10 second
Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
The result will be the desired IWebElement or Null
Unfortunately the getter to get the current value from ImplicitWait is not implemented since 2016. So you can only reset the value to a fixed value and not dynamically read it before.
another solution would be use an explicit wait. In Java (pseudo-code; you'd have to translate) something like this would work:
wait.until(ExpectedConditions.not(ExpectedConditions.invisibilityOfElementLocated(by)), 10, 1000)
that will wait until the element appears on the page, polling every second for ten seconds.
In javascript, it's :
driver.manage().timeouts().implicitlyWait(10);
I'm trying to write my own ExpectedConditions for Selenium but I don't know how to add a new one. Does anyone have an example? I can't find any tutorials for this online.
In my current case I want to wait until an element exists, is visible, is enabled AND doesn't have the attr "aria-disabled". I know this code doesn't work:
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(seconds));
return wait.Until<IWebElement>((d) =>
{
return ExpectedConditions.ElementExists(locator)
&& ExpectedConditions.ElementIsVisible
&& d.FindElement(locator).Enabled
&& !d.FindElement(locator).GetAttribute("aria-disabled")
}
EDIT: A little additional info: the problem I am running into is with jQuery tabs. I have a form on a disabled tab and it will start filling out fields on that tab before the tab becomes active.
An "expected condition" is nothing more than an anonymous method using a lambda expression. These have become a staple of .NET development since .NET 3.0, especially with the release of LINQ. Since the vast majority of .NET developers are comfortable with the C# lambda syntax, the WebDriver .NET bindings' ExpectedConditions implementation only has a few methods.
Creating a wait like you're asking for would look something like this:
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until<IWebElement>((d) =>
{
IWebElement element = d.FindElement(By.Id("myid"));
if (element.Displayed &&
element.Enabled &&
element.GetAttribute("aria-disabled") == null)
{
return element;
}
return null;
});
If you're not experienced with this construct, I would recommend becoming so. It is only likely to become more prevalent in future versions of .NET.
I understand the theory behind ExpectedConditions (I think), but I often find them cumbersome and difficult to use in practice.
I would go with this sort of approach:
public void WaitForElementPresentAndEnabled(By locator, int secondsToWait = 30)
{
new WebDriverWait(driver, new TimeSpan(0, 0, secondsToWait))
.Until(d => d.FindElement(locator).Enabled
&& d.FindElement(locator).Displayed
&& d.FindElement(locator).GetAttribute("aria-disabled") == null
);
}
I will be happy to learn from an answer that uses all ExpectedConditions here :)
Since all of these answers point the OP to use a separate method with a new wait and encapsulate the function instead of actually using a custom expected conditions, I'll post my answer:
Create a class CustomExpectedConditions.cs
Create each one of your conditions as static accessible methods that you can later call from your wait
public class CustomExpectedConditions
{
public static Func<IWebDriver, IWebElement> ElementExistsIsVisibleIsEnabledNoAttribute(By locator)
{
return (driver) =>
{
IWebElement element = driver.FindElement(locator);
if (element.Displayed
&& element.Enabled
&& element.GetAttribute("aria-disabled").Equals(null))
{
return element;
}
return null;
};
}
}
Now you can call it as you would any other expected condition like so:
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(TIMEOUT));
wait.Until(CustomExpectedConditions.ElementExistsIsVisibleIsEnabledNoAttribute(By.Id("someId")));
I've converted an example of WebDriverWait and ExpectedCondition/s from Java to C#.
Java version:
WebElement table = (new WebDriverWait(driver, 20))
.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("table#tabletable")));
C# version:
IWebElement table = new WebDriverWait(driver, TimeSpan.FromMilliseconds(20000))
.Until(ExpectedConditions.ElementExists(By.CssSelector("table#tabletable")));
I want to find a textbox id= UserName and give it a value =sa,
There is something wrong with my testing.
The error show UnexpectedJavaScriptError.
What's going on? How can I solve this?
Here is my code.
public void SetupTest()
{
driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 30));
driver.Navigate().GoToUrl(**the WEBSITE url**);
}
public void Test1()
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript("document.getElementById('UserName').value='sa'");
}
thanks
I use Selenium at my day job, and this sort of code should work fine.
I think the problem may be that the element isn't there when you are trying to use it.
My suggestion is to try something like this:
try {
var element = document.getElementById('UserName');
if(element) {
element.value = 'sa';
}
} catch(e) {}
I actually have a "jquery" helper method in our selenium code that uses jQuery to find elements and then returns the [0] element so that we can use it with Selenium API.
private IWebElement GetFirstElement()
{
return (IWebElement)((IJavaScriptExecutor)_driver).ExecuteScript("return $(\"" + _selector + "\")[0];", null);
}
Additionally, you might just need to wait until the element is on the screen.
An easy way to do this is to use Selenium's FindElement(By...) because Selenium will wait a configurable amount of time for the element to appear.
If you do this, it might make more sense to avoid JavaScript altogether for this case, but what you are trying to do should work in a perfect scenario.
You cannot access DOM using selenium webdriver. Instead of using javascript you can achieve the same using the below code snippet
string text = "sa";
IWebElement element = driver.FindElement(By.Id("UserName"));
element.SendKeys(text);