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);
}
}
Related
I got his code to wait until control found
WebDriverWait wait = new WebDriverWait(Browser, TimeSpan.FromSeconds(20));
var ok = wait.Until(ExpectedConditions.PresenceOfAllElementsLocatedBy(By.XPath("(//button[#id='btnOkDialog'])[" + buttonOccuranceNo + "]")));
I use this when some control take place in dom after any event.
Now if I reduce scope of search in page [Like taking a div inside WebDriver, which is a WebElement] then how do I search any control within that scope until its found or timeout occurs?
You need to setup a different wait, using the DefaultWait class:
var waitInnerScope = new DefaultWait<IWebElement>(divInsideWebDriver);
waitInnerScope.Timeout = TimeSpan.FromSeconds(timeout);
waitInnerScope.IgnoreExceptionTypes(typeof(NoSuchElementException));
Then use with wait.Until:
var ok = waitInnerScope.Until(PresenceOfAllElementsLocatedBy(By.XPath("(//button[#id='btnOkDialog'])[" + buttonOccuranceNo + "]")));
EDIT: Since ExpectedConditions consists of static methods that implement Func<IWebDriver, ...> for the use with WebDriverWait, you'll have to use your own method that takes an IWebElement as so:
public static Func<IWebElement, ReadOnlyCollection<IWebElement>> PresenceOfAllElementsLocatedBy(By locator)
{
return (element) =>
{
try
{
var elements = element.FindElements(locator);
return elements.Any() ? elements : null;
}
catch (StaleElementReferenceException)
{
return null;
}
};
}
Or you can use a lambda expression for an anonymous method like so:
waitInnerScope.Until<ReadOnlyCollection<IWebElement>>((element) =>
{
try
{
var elements = element.FindElements(By.XPath("(//button[#id='btnOkDialog'])[" + buttonOccuranceNo + "]"));
return elements.Any() ? elements : null;
}
catch (StaleElementReferenceException)
{
return null;
}
});
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 am using Wait.Until method to check if my page is already loaded or if it still loading .
This is how it looks like :
protected IWebElement FindElement(By by, int timeoutInSeconds)
{
StackTrace stackTrace = new StackTrace();
string callingMethod = stackTrace.GetFrame(1).GetMethod().Name;
string message = "Error finding element in method: " + callingMethod;
if (timeoutInSeconds > 0)
{
try
{
WebDriverWait wait = new WebDriverWait(chromeDriver, TimeSpan.FromSeconds(timeoutInSeconds));
wait.Until(ExpectedConditions.ElementIsVisible(by));
Thread.Sleep(800);
}
catch (Exception)
{
Assert(false, message);
throw new Exception(message);
}
}
return chromeDriver.FindElement(by);
}
But now we want to change our automation pages and start using FindBy foe every element , like this :
[FindsBy(How = How.Id, Using = "username")]
public IWebElement _logInUserName;
but wait.until needs "by" element .
I saw the abstract solution for this problem , but it is no good for my case .
can anyone know another solution that i can use ?
There is a ByFactory class in Selenium .NET solution. I took this implementation to achieve what you want:
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace SeleniumPlayground
{
public static class SeleniumHelper
{
public static FindsByAttribute GetFindsByAttributeFromField(Type pageObject, string iwebElementFieldName)
{
FieldInfo fi = pageObject.GetField(iwebElementFieldName);
FindsByAttribute attr = (FindsByAttribute)fi.GetCustomAttributes(typeof(FindsByAttribute), false).FirstOrDefault();
return attr;
}
public static By GeyByFromFindsBy(FindsByAttribute attribute)
{
var how = attribute.How;
var usingValue = attribute.Using;
switch (how)
{
case How.Id:
return By.Id(usingValue);
case How.Name:
return By.Name(usingValue);
case How.TagName:
return By.TagName(usingValue);
case How.ClassName:
return By.ClassName(usingValue);
case How.CssSelector:
return By.CssSelector(usingValue);
case How.LinkText:
return By.LinkText(usingValue);
case How.PartialLinkText:
return By.PartialLinkText(usingValue);
case How.XPath:
return By.XPath(usingValue);
case How.Custom:
if (attribute.CustomFinderType == null)
{
throw new ArgumentException("Cannot use How.Custom without supplying a custom finder type");
}
if (!attribute.CustomFinderType.IsSubclassOf(typeof(By)))
{
throw new ArgumentException("Custom finder type must be a descendent of the By class");
}
ConstructorInfo ctor = attribute.CustomFinderType.GetConstructor(new Type[] { typeof(string) });
if (ctor == null)
{
throw new ArgumentException("Custom finder type must expose a public constructor with a string argument");
}
By finder = ctor.Invoke(new object[] { usingValue }) as By;
return finder;
}
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Did not know how to construct How from how {0}, using {1}", how, usingValue));
}
}
And here's an example usage:
public class Page
{
private IWebDriver driver;
[FindsBy(How = How.Id, Using = "content")]
public IWebElement ele;
public Page(IWebDriver _driver)
{
this.driver = _driver;
}
}
Use as follows:
Page page = PageFactory.InitElements<Page>(driver);
FindsByAttribute findsBy = SeleniumHelper.GetFindsByAttributeFromField(typeof(Page), "ele");
By by = SeleniumHelper.GeyByFromFindsBy(findsBy);
I found a way to o this :)
public static IWebElement FindElement( IWebElement element, int timeoutInSeconds)
{
if (timeoutInSeconds > 0)
{
var wait = new WebDriverWait(chromeDriver, TimeSpan.FromSeconds(timeoutInSeconds));
return wait.Until(drv => element);
}
return element;
}
We faced the same issue when using selenium for testing. So we created a mini framework on top of selenium which keeps trying to do (whatever you are trying to do with selenium). Or otherwise you can provide a custom pre or post condition.
https://github.com/LiquidThinking/Xenon
It is very simple to setup and all information is available on github, plus it comes with Screen objects which can help to reuse your code.
For example
new XenonTest(new SeleniumXenonBrowser())
.GoToUrl("http://www.google.co.uk", a => a.PageContains("google") );
So in this example, we added a pre wait condition which says "before going to google.co.uk make sure that the current page contains "google" in page source. Which is obviously incorrect way to do it but it explains how to use pre or post wait conditions.
If you do not specify any wait condition then for some actions, there is a default wait action. for e.g. https://github.com/LiquidThinking/Xenon/blob/master/Xenon/BaseXenonTest.cs#L72
See how we check if a customPreWait is available for "Click" and if not then we added a custom pre-wait to check if that css selectors exists on the page before performing the "actual click action".
Hope it will help you, it is on nuget or otherwise just use the code which you want. it is under MIT license.
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;
});
}