Using explicit waits

The recommended solution for waiting problems is to use explicit waits. There is already a class full of precanned examples called ExpectedConditions to make your life easy, and it really is not that hard to use them. You can do the simple things, such as finding an element once it becomes visible in two lines of code:

WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);
WebElement myElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.id("foo")));

Bear in mind that the ExpectedConditions class are prime examples. While being helpful, they are really designed to show you how to set explicit waits up so that you can easily create your own. With these examples, it is trivial to create a new class with conditions that you care about in it, which can be reused again and again in your project.

Earlier, we said we would look at a way to work out if your site had finished processing AJAX requests; let's do that now. First of all, we will create a new class that lets you work out whether a website that uses jQuery has finished making AJAX calls, by using this code:

package com.masteringselenium;

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;

public class AdditionalConditions {

public static ExpectedCondition<Boolean>
jQueryAJAXCallsHaveCompleted() {
return new ExpectedCondition<Boolean>() {

@Override
public Boolean apply(WebDriver driver) {
return (Boolean) ((JavascriptExecutor)
driver).executeScript("return
(window.jQuery != null) && (jQuery.active === 0);");
}
};
}
}

What this will do is use JavascriptExecutor to make a request to the page to find out whether jQuery has any outstanding active AJAX requests. There is also some protection built in to ensure that if the page does not initially have jQuery loaded when ExpectedCondition fires, the JavaScript snippet will not error.  The ExpectedCondition class will still obviously throw an exception if it times out.

We can now call this condition anywhere in our code by using the following:

WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);
wait.until(AdditionalConditions.jQueryAJAXCallsHaveCompleted()));

We now have an easy way to find out whether jQuery has finished running AJAX calls in the background, before we interact with our jQuery-based site.

Maybe you don't use jQuery; well, how about AngularJS? Take a look at this code:

public static ExpectedCondition<Boolean> angularHasFinishedProcessing() {
return new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
return Boolean.valueOf(((JavascriptExecutor)
driver).executeScript("return
(window.angular !== undefined) &&
(angular.element(document).injector()
!== undefined) && (angular.element(document).injector()
.get('$http').pendingRequests.length === 0)").toString());
}
};
}

This one is a little more complex on the JavaScript side. We have a chain of conditions to ensure that AngularJS is available, and has had time to bootstrap and generate its services. We then hook into the internal pendingRequests array and count the number of AJAX requests that still need to complete. We are using some internal knowledge of Angular for this example, so your mileage may vary, but it should be easy enough to tweak it if Angular does change the way it tracks pending requests.

As with our previous example, this is now trivial to use elsewhere in your code; you just need the following:

WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);
wait.until(AdditionalConditions.angularHasFinishedProcessing());

As you can see, it really is quite easy to create new conditions to use in your code. The next question is, how complex can we make these waits?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.118.99