How to work with WebElement which change in runtime? - c#

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;
}
});

Related

Wait until class is removed from a div

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);
}
}

How to use Wait.Until with Selenium if I already have the element with FindsBy

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.

How to check for non existence of element using selenium web driver in C#

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;
}

How to check if an element exists?

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;
}
}

webdriver - Utility / helper method - wait until class name contains specific style attribute

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;
});
}

Categories