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")));
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 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.
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 seen many posts on handling switching between frames in Selenium but they all seem to reference the Java 'ExpectedConditions' library for the below method.
ExpectedConditions.frameToBeAvailableAndSwitchToIt
I was wondering if there is any C# implementation anywhere or if anyone has any such work around?
Cheers
There isn't a direct equivalent in the C# bindings but it's very easy to do this yourself.
Remember that Selenium is open source so let's dig out the source code. Here is the Java ExpectedConditions and here is the C# set.
So what's the Java version doing? Well, not a lot I tell you.
try {
return driver.switchTo().frame(frameLocator);
} catch (NoSuchFrameException e) {
return null;
}
All it's doing is attempting to switch to the frame you tell it to, and providing it was successful (as in, there was no exception in attempting to do that), then it's assumed it can carry on.
So, all you'll need to do is do the same thing in C#, so something like (not compiled):
public static Func<IWebDriver, bool> WaitUntilFrameLoadedAndSwitchToIt(By byToFindFrame)
{
return (driver) =>
{
try
{
return driver.SwitchTo().Frame(driver.FindElement(byToFindFrame));
}
catch (Exception)
{
return null;
}
return true;
};
}
As in, keep the same concept: try to find the frame and switch to it, any exceptions then we return null and force the caller (usually a WebDriverWait instance) to iterate through again. Returning true will tell the caller that we are happy we can move on.
All the waiting & expected conditions classes live in the OpenQA.Selenium.Support.UI namespace which lives in the WebDriver.Support.dll assembly.
These answers are old and I had the same issue. I was able to use SeleniumExtras.WaitHelpers.ExpectedConditions from nuget to achieve this easily.
//wait for 10 seconds max for the frame
WebDriverWaitwait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.FrameToBeAvailableAndSwitchToIt(By.Id("FRAMEID")));
I have just committed such a ugly code. Dropping here for the future!
protected void SwitchToFrame(int iframe = 1)
{
var driver = GetWebDriver();
driver.SwitchTo().DefaultContent();
bool done = false, timeout = false;
int counter = 0;
do
{
counter++;
try
{
driver.SwitchTo().Frame(iframe);
done = true;
}
catch (OpenQA.Selenium.NoSuchFrameException)
{
if (counter <= Constants.GLOBAL_MAX_WAIT_SEC)
{
Wait(1);
continue;
}
else timeout = true;
}
} while (!done && !timeout);
if (timeout) throw new OpenQA.Selenium.NoSuchFrameException(iframe.ToString());
}