I am writing some Java Webdriver code to automate my application. How can I correctly check whether the page has been loaded or not? The application has some Ajax calls, too
Below is some code from my BasePageObject class for waits:
public void waitForPageLoadAndTitleContains(int timeout, String pageTitle) {
WebDriverWait wait = new WebDriverWait(driver, timeout, 1000);
wait.until(ExpectedConditions.titleContains(pageTitle));
}
public void waitForElementPresence(By locator, int seconds) {
WebDriverWait wait = new WebDriverWait(driver, seconds);
wait.until(ExpectedConditions.presenceOfElementLocated(locator));
}
public void jsWaitForPageToLoad(int timeOutInSeconds) {
JavascriptExecutor js = (JavascriptExecutor) driver;
String jsCommand = "return document.readyState";
// Validate readyState before doing any waits
if (js.executeScript(jsCommand).toString().equals("complete")) {
return;
}
for (int i = 0; i < timeOutInSeconds; i++) {
TimeManager.waitInSeconds(3);
if (js.executeScript(jsCommand).toString().equals("complete")) {
break;
}
}
}
/**
* Looks for a visible OR invisible element via the provided locator for up
* to maxWaitTime. Returns as soon as the element is found.
*
* @param byLocator
* @param maxWaitTime - In seconds
* @return
*
*/
public WebElement findElementThatIsPresent(final By byLocator, int maxWaitTime) {
if (driver == null) {
nullDriverNullPointerExeption();
}
FluentWait<WebDriver> wait = new FluentWait<>(driver).withTimeout(maxWaitTime, java.util.concurrent.TimeUnit.SECONDS)
.pollingEvery(200, java.util.concurrent.TimeUnit.MILLISECONDS);
try {
return wait.until((WebDriver webDriver) -> {
List<WebElement> elems = driver.findElements(byLocator);
if (elems.size() > 0) {
return elems.get(0);
} else {
return null;
}
});
} catch (Exception e) {
return null;
}
}
Supporting methods:
/**
* Gets locator.
*
* @param fieldName
* @return
*/
public By getBy(String fieldName) {
try {
return new Annotations(this.getClass().getDeclaredField(fieldName)).buildBy();
} catch (NoSuchFieldException e) {
return null;
}
}
Here is how I would fix it, using a code snippet to give you a basic idea:
public class IFrame1 extends LoadableComponent<IFrame1> {
private RemoteWebDriver driver;
@FindBy(id = "iFrame1TextFieldTestInputControlID" ) public WebElement iFrame1TextFieldInput;
@FindBy(id = "iFrame1TextFieldTestProcessButtonID" ) public WebElement copyButton;
public IFrame1( RemoteWebDriver drv ) {
super();
this.driver = drv;
this.driver.switchTo().defaultContent();
waitTimer(1, 1000);
this.driver.switchTo().frame("BodyFrame1");
LOGGER.info("IFrame1 constructor...");
}
@Override
protected void isLoaded() throws Error {
LOGGER.info("IFrame1.isLoaded()...");
PageFactory.initElements( driver, this );
try {
assertTrue( "Page visible title is not yet available.",
driver.findElementByCssSelector("body form#webDriverUnitiFrame1TestFormID h1")
.getText().equals("iFrame1 Test") );
} catch ( NoSuchElementException e) {
LOGGER.info("No such element." );
assertTrue("No such element.", false);
}
}
/**
* Method: load
* Overidden method from the LoadableComponent class.
* @return void
* @throws null
*/
@Override
protected void load() {
LOGGER.info("IFrame1.load()...");
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring( NoSuchElementException.class )
.ignoring( StaleElementReferenceException.class ) ;
wait.until( ExpectedConditions.presenceOfElementLocated(
By.cssSelector("body form#webDriverUnitiFrame1TestFormID h1") ) );
}
....
You can get the HTML of the website with driver.getPageSource(). If the html does not change in a given interval of time this means that the page is done loading. One or two seconds should be enough. If you want to speed things up you can just compare the lenght of the two htmls. If their lenght is equal the htmls should be equal and that means the page is fully loaded. The JavaScript solution did not work for me.