How to add expected condition to function which finds element - c#

I have code which finds element with delay, but sometimes element is already but not clickable and not available in DOM, so what i should to add to my code to check those arguments
public IWebElement FindElement(IWebDriver driver, By howBy, int timeoutInSeconds = 10)
{
TimeSpan elementTimeOut = TimeSpan.FromSeconds(20);
IWebElement elementfound = null;
try
{
WebDriverWait wait = new WebDriverWait(driver, elementTimeOut);
elementfound = wait.Until<IWebElement>(d =>
{
try
{
elementfound = driver.FindElement(howBy);
}
catch (NoSuchElementException e)
{
Debug.WriteLine("Please fail NoSuchElementException");
throw;
}
return elementfound;
});
}
catch (WebDriverTimeoutException e)
{
Debug.WriteLine("Please fail WebDriverTimeoutException");
throw;
}
return elementfound;
}

Firstly, It'll check if it's 'visible' by using the standard ExpectedConditions.visibilityOfElementLocated, it'll then simply check if the element.isEnabled() is true or not.
This can be condensed slightly, this basically means (simplified, in C#):
Wait until the element is returned from the DOM
Wait until the element's .Displayed property is true (which is essentially what visibilityOfElementLocated is checking for).
Wait until the element's .Enabled property is true (which is essentially what the elementToBeClickable is checking for).
I would implement this like so (adding onto the current set of ExpectedConditions, but there are multiple ways of doing it:
// <param name="locator">The locator used to find the element.</param>
// <returns>The <see cref="IWebElement"/> once it is located, visible and clickable.</returns>
public static Func<IWebDriver, IWebElement> ElementIsClickable(By locator)
{
return driver =>
{
var element = driver.FindElement(locator);
return (element != null && element.Displayed && element.Enabled) ? element : null;
};
}
Above method can be used something like:
var wait = new WebDriverWait(driver, TimeSpan.FromMinutes(1));
var clickableElement = wait.Until(ExpectedConditions.ElementIsClickable(By.Id("id")));
However, you can pass howBy as the param of ElementIsClickable() method.

elementToBeClickable is used to wait for an element to be clickable .
Please use the following code inside your method.
elementfound = wait.until(ExpectedConditions.elementToBeClickable(howBy);

Related

Selenium C# Code Works But Need Waits or Delays

By adding a while(ele == null) loop, I got the code to run and work most of the time out of the debugger. Pretty ugly. This leads me to think that I need to over ride the FindElements() function using a wrapper but have no idea how to do this to add some delay. There is an example at Explicit Wait for findElements in Selenium Webdriver but it's written in JavaScript. I put that example in the code below. Can some one guide me on this?
public void WriteAPost()
{
ele = driver.FindElements(By.CssSelector(".a8c37x1j.ni8dbmo4.stjgntxs.l9j0dhe7.ltmttdrg.g0qnabr5.ojkyduve")).FirstOrDefault(x => x.Text == "Create Post");
while(ele == null)
{
ele = driver.FindElements(By.CssSelector(".a8c37x1j.ni8dbmo4.stjgntxs.l9j0dhe7.ltmttdrg.g0qnabr5.ojkyduve")).FirstOrDefault(x => x.Text == "Create Post");
}
ele.Click();
Thread.Sleep(3000);
ele = driver.SwitchTo().ActiveElement();
PClipboard.SetText("Post text to use for Text Area");
ele.SendKeys(OpenQA.Selenium.Keys.Control + 'v');
Thread.Sleep(3000);
ele = driver.FindElements(By.XPath("//div[#role = 'button']")).FirstOrDefault(x => x.Text == "Post");
while (ele == null)
{
ele = driver.FindElements(By.XPath("//div[#role = 'button']")).FirstOrDefault(x => x.Text == "Post");
}
ele.Click();
Thread.Sleep(3000);
driver.Quit();
}
static class PClipboard
{
public static void SetText(string p_Text)
{
Thread STAThread = new Thread(
delegate ()
{
// Use a fully qualified name for Clipboard otherwise it
// will end up calling itself.
System.Windows.Forms.Clipboard.SetText(p_Text);
});
STAThread.SetApartmentState(ApartmentState.STA);
STAThread.Start();
STAThread.Join();
}
}
}
// Javascript FindElements() wrapper
/// <summary>
/// Allows you to execute the FindElements call but specify your own timeout explicitly for this single lookup
/// </summary>
/// <remarks>
/// If you want no timeout, you can pass in TimeSpan.FromSeconds(0) to return an empty list if no elements match immediately. But then you may as well use the original method
/// </remarks>
/// <param name="driver">The IWebDriver instance to do the lookup with</param>
/// <param name="findBy">The By expression to use to find matching elements</param>
/// <param name="timeout">A timespan specifying how long to wait for the element to be available</param>
public static ReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By findBy, TimeSpan timeout)
{
var wait = new WebDriverWait(driver, timeout);
return wait.Until((d) =>
{
var elements = d.FindElements(findBy);
return (elements.Count > 0)
? elements
: null;
});
}
You can wait for a condition to be met:
new WebDriverWait(driver, timeout).Until(ExpectedConditions.ElementExists((By.Id(id))));
or you can wait implicitly:
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(timeInSeconds);
I just don't know where in the code to wait.
Personally, my Selenium code is full of "test failure reporting" tests and try-catch blocks which report where the failure happened. If you model your code this way, it should narrow down the problem and clue you to where waits are needed.

Can not catch WebDriverException for certain elements

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.

Container test with Selenium in C#

How do I continue the selenium test if an element does not exist, in c#?
if (type == "ID" && Event == "Click") {
Thread.Sleep(TimeSpan.FromSeconds(time));
WebDriverWait wait = new WebDriverWait(webDriver,
TimeSpan.FromSeconds(20));
IWebElement element =
wait.Until(ExpectedConditions.ElementIsVisible(By.Id(id)));
element.Click();
}
else if (type == "ID") {
webDriver.FindElement(By.Id(id)).SendKeys(key[data]);
Thread.Sleep(TimeSpan.FromSeconds(time));
}
I want to skip the selenium element if it's not on the page.
You may use exception handling to avoid test fail when there is no element
try
{
webDriver.FindElement(By.Id(id));
}
catch(NoSuchElementException)
{
}
Or use array, since finding multiple elements do not cause exception, just empty array.
IWebElement[] elements = webDriver.FindElements(By.Id(id)).ToArray();
if (elements.Count() != 0)
{
do what you want
}

Possible to make generic function to find a particular item with Selenium WebDriver?

I would like to make a generic function to find a particular item?
I do this C# code (Src : Use Explicit Waits in Selenium WebDriver Correctly)
IWebDriver driver = new FirefoxDriver();
driver.Url = "http://somedomain/url_that_delays_loading";
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
{
try
{
return d.FindElement(By.Id("someDynamicElement"));
}
catch
{
return null;
}
});
I would replace wait.Until((....); using a generic function.
How can Ideclare a generic By selector to find a particular item?
Here is a list of the built-in Selenium2 selectors:
ClassName
CssSelector
Id
LinkText
PartialLinkText
Name
TagName
XPath
For example :
IWebElement myDynamicElement = WaitForElement(????) // for exemple By.TagName = "Test"
public static IWebElement WaitForElement(**By selector**)
{
wait.Until<IWebElement>((d) =>
{
try
{
return d.FindElement(**By selector**);
}
catch
{
return null;
}
});
}
public static IWebElement WaitForElement(IWebDriver driver, By selector)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10);
return wait.Until<IWebElement>(d =>
{
try
{
return d.FindElement(selector);
}
catch
{
return null;
}
});
}
This would be called with something like:
WaitForElement(driver, By.Id("someDynamicElement"));
You just give it an instance of a By selector. The underlying d.FindElement call will then deal with the rest, it will deal with figuring out what 'kind' of selector it is. You don't need to do much.
The whole concept behind the By class, is to make it generic in the first place. You will be duplicating work.
You will also have to pass in the driver as well, unless you plan to have it as a static field or an extension method on the driver itself.

Selenium.WebDriver 2.32.1 C# - Wait untill LOADING DIV is hidden after Page Load

I am really sorry if this question has already been asked/answered. but I could not find it.
Please excuse my ignorance as I am new to WebDriver.
When the page is initially loads, it displays a LOADING DIV untill all the data is loaded. How can I wait until this div is hidden before I proceed with other actions on page elements?
I am trying to know as follows:
public static void waitForPageLoad(string ID, IWebDriver driver)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id(ID));
});
}
I pass the Id of SOME OTHER ELEMENT to this function which I will use when the LOADING DIV disappears. It returns the wrong result as the element by ID is actually present/loaded but is behind the grey DIV that shows "Loading... Please wait" message. So this does not work. I would like to know when that LOADING div disappears.
Any help is greatly appreciated.
By waiting on a bool value instead of IWebElement, the .NET WebDriverWait class will wait until a value of true is returned. Given that, how about trying something like the following:
public static void WaitForElementToNotExist(string ID, IWebDriver driver)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until<bool>((d) =>
{
try
{
// If the find succeeds, the element exists, and
// we want the element to *not* exist, so we want
// to return true when the find throws an exception.
IWebElement element = d.FindElement(By.Id(ID));
return false;
}
catch (NoSuchElementException)
{
return true;
}
});
}
Note that this is the appropriate pattern if the element you're looking for is actually removed from the DOM. If, on the other hand, the "waiting" element is always present in the DOM, but just made visible/invisible as required by the JavaScript framework your app is using, then the code is a little simpler, and looks something like this:
public static void WaitForElementInvisible(string ID, IWebDriver driver)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until<bool>((d) =>
{
try
{
IWebElement element = d.FindElement(By.Id(ID));
return !element.Displayed;
}
catch (NoSuchElementException)
{
// If the find fails, the element exists, and
// by definition, cannot then be visible.
return true;
}
});
}

Categories

Resources