I have a table with several rows. Some of these rows may have a specific element and others may not. For sure some will and some won't.
I find the row and have it into a WebElement. Now to see whether an element is there I do the following (assume xp = ".//someelement)
List<WebElement> eles = row.findElements(By.xpath(xp));
if (eles.size() == 0) {
// element is not there
} else {
// element is there
}
This is fine when the element is present. When it is not, it takes like 30 seconds or a minute to figure out that it is not there. If called often this can really slow down the test. I can do
try {
WebElement ele = row.findElement(by.xpath(xp));
} catch (Exception ex) {
// element is not there
}
using a more detailed Exception. This works fine too but same problem. It waits a minute or half a minute.
Is there a way to check more quickly whether an element is present or not? If it were relative to driver (driver.findElementBy()) instead of an element (row.findElementBy()) I think I might know how to do it.
This is Java.
In your first example where you have a List of Elements you are not trying to locate one element; but several (let's say a collection of rows instead of one row). The second element ele is finding (or trying to find) a specific item (let's say 1 row). Hence, ideally you should say in your comments that some elementS were not there for eles . Nevertheless, the time issue is probably down to an implicit or explicit wait. Read here for more.
I prefer the first way where you check for a collection of elements (so you can aim it at a xpath and find all the tags included (or none at all). Ideally though you should go for an explicit wait.
Here is the method for waiting, it will return true/or false based on if the element was present during the polling time (10sec for example); worth noting that if the element is found as present earlier than the 10sec limit the loop will break and will return true. Of course you can play with timeOut to achieve the desired result; don't go to fast though (like 2 sec) otherwise you are risking your test occasionally failing because the tables were not loaded yet:
public boolean waitForElement(String elementXpath, int timeOut) {
try{
WebDriverWait wait = new WebDriverWait(driver, timeOut);
boolean elementPresent=wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(elementXpath)).isDisplayed());
System.out.printf("%nElement is present [T/F]..? ")+elementPresent;
}
catch(TimeoutException e1){e1.printStackTrace();elementPresent=false;}
return elementPresent;
}
I'm guessing that you are already using an explicit wait of 30sec for all of your findElement attempts hence the discrepancy.
Hope the above helps,
Best of luck!
Another option is to use WebDriverWait (explicit waits) rather than implicit ones.
This basically makes it so your tests will only wait a long time when you tell them too, otherwise they'll instantly fail if they don't find the elements you're looking for.
Adapted from slide52 of tourdedave's slideshare
// Use this class whenever you have to access the driver
// And you should only have to setDriver in a BeforeMethod when setting up.
// This method shows how to do it with a single browser
// This could be converted quite easily to threadlocals for parallel test runs
public class DriverManager {
private final WebDriver driver;
public static WebDriver getDriver() {
return driver;
}
public static setDriver(WebDriver driver) {
DriverManager.driver = driver;
}
public class WaitUntil {
public static Boolean displayed(By locator, Integer... timeout) {
try {
waitFor(ExpectedConditions.visibilityOfElementLocated(locator),
(timeout.length = 0 : null ? timeout[0];
} catch (TimeoutException exception) {
return false;
}
return true;
}
// add additional methods you want to wait for
// Look at the source of the ExpectedConditions class to see how to create
// your own
// e.g. presence of element
// number of results from locator changes
// element done moving
// url changes, etc.
private static void waitFor(ExpectedCondition<WebElement> condition, Integer timeout) {
timeout = timeout != null ? timeout[0] : 5; //default timeout if no value given
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(condition);
}
}
Then in any class you can
By submitButtonBy = By.cssSelector(".submit);
use WaitUntil.displayed(submitButtonBy);
And it will wait for 5 seconds. If you want to wait for 10:
use WaitUntil.displayed(submitButtonBy, 10);
The nice thing about making a class with a bunch of methods like this is it's easy to add additional exceptions so you can choose to return false if there's a stale element or something else, rather than have to deal with a try catch in page classes or test classes.
Related
I am automating a scenario wherein a specific web element might or might not be displayed on the UI. If it is getting displayed, then I want to perform a specific action on it. I am using below logic for the same
try{
if(element.isDisplayed())
{
//perform action on the element if it is visible
}
}
catch(Exception e)
{
}
The code works fine whenever the element is visible on the UI. But during scenarios when the element is not displayed, then 'element.isDisplayed()' waits for the element for 10 seconds (i.e the implicit wait time which I have defined for the driver session).
I want my script to not wait for that 10 seconds for the element to appear, instead just go ahead with further actions. Any idea what approach should I go with here?
It seem like you can achieve with decrease timeout value utilize WebDriverWait.
Try to create a boolean function to check the element presence or not with specific time.
public boolean checkElement(By locator, int seconds) {
boolean find = false;
try {
new WebDriverWait(driver, seconds).until(ExpectedConditions.presenceOfElementLocated(locator));
find = true;
} catch (Exception e) {
// TODO: handle exception
}
return find;
}
Just call with:
By locator = By.name("yourLocator");
if(checkElement(locator, 1)) {
//perform here
....
}
The following imports:
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
Please delete implicitWait which you've declared before.
Regarding the WebDriverWait data type, I am assuming that only one instance is needed of such data type, which means I could potentially create a wrapper and allow only one instance creation by using a singleton pattern approach.
At the present time I used thread.sleep and basically everywhere I need that function to be called I am extending from the class which probably not the best approach. Also of course I should be using WebDriverWait instead of thread. What should the approach be?
So far I created a page object with web elements and a separate service for the logic itself, so now I need also WebDriverWait in every service since it’s a necessary operation.
A cookie window that gets popped up once customer opened the page:
/**
* acceptCookies -> clickable
* cookieBanner -> just to identify that cookie component showed up.
* PageFactory -> will initialize every WebElement variable with a reference to a corresponding element on the actual web page.
*/
public class CookieModal {
WebDriver driver;
#FindBy(css = ".cookie-accept-all")
public WebElement acceptCookies;
public CookieModal(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
}
Then I separated the service (actions):
public class CookieService {
private final CookieModal cookieModal;
public CookieService(WebDriver driver) {
this.cookieModal = new CookieModal(driver);
}
public void acceptCookies() {
cookieModal.acceptCookies.click();
}
}
This must be changed to WebDriverWait, but I also think extending from AbstractPage in every page object is not necessary. Is my structure OK and how should I initialise WebDriverWait?
public class AbstractPage {
// This is not good as thread sleep is not dynamic and you
// have to specify time yourself change to webdriver wait
private AbstractPage microsleep(Integer milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (Throwable e) {
String error = String.format("Unable to put thread to sleep (requested %d milliseconds).", milliseconds);
throw new RuntimeException(error, e);
}
return this;
}
public AbstractPage emulateWaitingUser() {
return microsleep(800);
}
public AbstractPage sleep(Integer seconds) {
return microsleep(1000 * seconds);
}
Selenium supports integrating explicit waits into page objects. This is achieved by using a special way of how you initialize your page. In your example you are doing this:
PageFactory.initElements(driver, this);
which involves some basic default way. However, you can add more complexity here, but you get a more effective architecture at the same time.
You can extend the AjaxElementLocator class where you will override isElementUsable method in the way that would involve any sort of condition and waits. Then you will be initializing your page(s) with that locator through a dedicated LocatorFactory. Some example of how to use all that classes you can find here.
WebDriverWait is commonly used with the ExpectedConditions class. In that case, you cannot just wait for 800 ms. You need to wait until a condition is met. For example, wait until page title is displayed, or wait until loader icon is not visible, wait until login button is clickable, etc.
That means if you wish to instantiate a wait in an abstract method, you will need to add an unknown (and non-abstract) wait condition. You could just instantiate a general wait object and then add a condition when it is known, but it seems a bit incomplete.
Another idea that comes to my mind is declaring WebDriverWait as a field in Cookie Service class and pass it to its methods.
I am trying to automate an application which involves CAPTCHA.
I tried to use WebDriverWait with below code:
WebDriverWait mywait = new WebDriverWait(driver,20);
mywait.until(ExpectedConditions.attributeToBeNotEmpty(driver.findElementByClassName("loginCaptcha"),"value"));
Now the problem here is as soon as I enter the first character of captcha in the text, the above condition becomes true and the next statements are getting executed which results in invalid captcha error.
Is there any way to wait until 5 characters are entered in the text box(my captcha is always 5 characters fixed in length.)
PS: I dont want to use static waits.
First of all, you should not try and solve CAPTCHA with selenium. The whole purpose of CAPTCHA is to prevent UI automation. If you want to overcome CAPTCHA, you should use internal APIs of your SUT.
Regarding waiting for specific text length, it should be something like:
//First init WebDriverWait to 20 sec
WebDriverWait mywait = new WebDriverWait(driver, 20);
//Locator to your DOM element
By byClass = By.class("loginCaptcha");
//Wait until text box has value greater or equal 5 character
mywait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return (d.findElement(byClass).getAttribute ("value").length() >= 5)
}
});
You can do this by using the following:
FluentWait
private Wait<WebDriver> wait = new FluentWait<>(yourWebDriverInstance).withTimeout(10, TimeUnit.SECONDS).pollingEvery(500, TimeUnit.MILLISECONDS).ignoring(NoSuchElementException.class);
A custom expected conditions method:
private Function<WebDriver, Boolean> expectedConditions(boolean condition) {
return driver -> condition;
}
Method calling the custom expected conditions:
public void waitUntilElemValueLengthEquals(WebElement element, int length) {
try {
wait.until(expectedConditions(element.getAttribute("value").length == length));
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
throw e;
}
}
USAGE:
WebElement e = driver.findElementByClassName("loginCaptcha");
waitUntilElemValueLengthEquals(e,5);
SIDE NOTE:
For an actual implementation, it would be good if you can create a class which implements the WebDriver interface. That class will contain all the WebDriver interface methods AND all your custom methods (like the ones above).
You should use some expected condition like to wait until the submit button is clickable if it's grayed out until you typed 5 characters or to save in a variable the fifth captcha character and then to wait until the this fifth character is typed in the input element.
In my learning curve I have been looking at the right way to wait for an element to be loaded and you get lots of pages on google.
Got down to 2 but in my view Method2(ExpectedConditions.ElementIsVisible) is more elegant and does what method1 is trying to achieve do you agree or is there a better way?
Method 1
public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
{
if (timeoutInSeconds > 0)
{
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
return wait.Until(drv => drv.FindElement(by));
}
return driver.FindElement(by);
}
Method 2
public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
var element = wait.Until(ExpectedConditions.ElementIsVisible(by));
return element;
}
Any suggestions or improvements also what happens if "NoFoundElement exception is thrown is it already handled or should I ignore it?
I would say yes, "Method 2" is the preferred way of doing it. Simply because if something is already implemented in WebDriver, there's no need for you to reimplement it in your testing framework.
As to your question regarding the NotFoundException: If the condition that you're waiting for isn't met after the specified timeout, the WebDriverWait will raise a WebDriverTimeoutException. Depending on the condition that you wanted to wait for, the WebDriverTimeoutException will have an inner exception with more details. If for example you use ExpectedConditions.ElementIsVisible(By.Id("myid")) and the element couldn't be located at all, the inner exception will be a NoSuchElementException. If the element could be located but isn't visible after the given timeout you'll just get the WebDriverTimeoutException.
Depending on what you want to do once you're sure that the element is "there", you can also use different ExpectedConditions. If the element you're waiting for is a button and you want to click on it, you can use ExpectedConditions.ElementToBeClickable as this will not only wait for the element to get loaded into the DOM and visible, but also wait for the element to get enabled.
I'm using Selenium Webdriver and have run into the following issue with my app under test.
The app has multiple pages each with an appropriate ".page-title" element which contains the name of the page (e.g. "Other Documents"). As the tests navigate around the app they assert that the browser is on the expected page using these elements before doing other stuff.
The issue is that if you click a button in the app which performs an action, then check that you're on the right page (e.g. check page-title element displays correct text), Webdriver doesn't wait for the action to be performed (e.g. new page load), it returns straight away and the test fails.
If you add a short thread sleep (500ms) between performing the action and checking you're on the right page, then you get StaleElementReferenceException (some of the time) and if you add a large thread sleep the test passes (but not quite all the time).
My aim is to reduce the flakiness of the tests, does anyone have a suggestion as to how I can do this without Thread.sleep?
instead of inserting thread.sleep method explicity
do try the WebDriver in built Implicitwait method..(C# code snippet)
Driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(Max_Time_Limit));
This makes WebDriver to wait till the element is visible/available. In case if it finds elements before the Max_Time_Limit, it snaps out of sleep mode and resumes the execution.
So no hassle of waiting till the Hard bound Max_Time_Limit.This way it helps speeding up your execution Time as well.
I hope this helps...All the best :-)
Try using this wait: using this you can wait for max time 15 secs/wait for the expected condition to be true i.e. wait for some element to be present.
You can give the xpath of some element on the next page, when that element is visible then the next step will be executed.
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("\xpath of some element on the next page")));
//Assert page title
driver.getTitle();
use fluentWait mechanism . Considered to be a robust approach. As documentation on fluent wait gives:
An implementation of the Wait interface that may have its timeout and polling interval configured on the fly.
Each FluentWait instance defines the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition. Furthermore, the user may configure the wait to ignore specific types of exceptions whilst waiting, such as NoSuchElementExceptions when searching for an element on the page.
details you can get here
here is the code of method I use:
public WebElement fluentWait(final By locator){
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(
new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
}
);
return foo; } ;
String xPathElement="...blablabla...";
fluentWait(By.xpath(xPathElement)).click();
//fluentWait(By.xpath(xPathElement)).getText();
Hope it works for you.
You can use Selenium's ExpectedCondition.
The code below (written in JAVA) waits for a maximum of inTimeout seconds for the element you want to appear. If the element appears sooner, it ends the wait.
public static void wait(WebDriver b,long inTimeout) {
final SlnDriver browser=b;
final long NO_LOADING_TIMEOUT = inTimeout;
class HasCondition implements ExpectedCondition<Boolean> {
#Override
public Boolean apply(WebDriver d) {
Boolean expected=false;
WebElement e = browser.findElement(By.xpath("blabla"));
if (e.getText().contains("TextYouWant")) {
expected= true;
break;
}
}
return expected;
}
}
}
for (;;) {
try {
new WebDriverWait(browser, NO_LOADING_TIMEOUT).until(new HasCondition());
} catch (TimeoutException e) {
return;
}
}
}