问题
When I run the following code, the execution abruptly ends unless I uncomment the Thread.sleep(). As result my code in the withdraw url servlet is not executed. The click is a submit button click which loads another page.
EventFiringWebDriver webDriver = new EventFiringWebDriver(new SafariDriver());
try {
webDriver.get("http://localhost:9988");
webDriver.findElement(By.id("amount")).sendKeys(new StringBuffer().append(amount.getRupees()).append('.').append(amount.getPaise()).toString());
webDriver.findElement(By.id("withdraw")).click();
//Thread.sleep(10000);
} finally {
webDriver.close();
}
What is the right way to make selenium wait till the page loads?
I am using the following selenium version
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-server</artifactId>
<version>3.11.0</version>
回答1:
Sounds like explicit waits might be what you need. There are a collection of ready-to-use conditions in the ExpectedConditions
class (see https://www.seleniumhq.org/docs/04_webdriver_advanced.jsp for more). But once you understand how they work, making your own is pretty straightforward. The beauty of these is that unlike Thread.sleep(), once the condition is met, it'll immediately stop waiting.
I wrote out some specific examples I've used in the past for waiting for "page loads", and then get a little into how to make your own just in case it's useful to you:
If your page is light on the ajax content, and loads some static content once, you could implement an explicit wait condition to check the DOM's readyState property
// this could benefit being static so it doesn't create new classpath defs every invocation
public ExpectedCondition<Boolean> readyStateIsComplete() {
return new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver webDriver) {
String readyState = (String) ((JavascriptExecutor) webDriver)
.executeScript("return document.readyState");
return readyState.equals("complete");
}
public String toString() {
return "page document's readyState flag to be complete";
}
};
}
This property only is "complete" once all static content is finished loading. Using it could be like:
// prepare me like this
WebDriverWait wait = new WebDriverWait(driver, maxSecondsToWait);
// Use me like this
wait.until(readyStateIsComplete());
The example above is good for the first time you load a page before all content finishes. If your page is heavy on the ajax content, maybe instead or in addition to you could try waiting for the ajax queue to reach zero, which can be done on an already loaded page that has ajax work it is doing.
public ExpectedCondition<Boolean> activeQueuesToFinish() {
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
// This is just a formatted javascript block with in-string indenting
String jScript = "if (A4J.AJAX) {" +
" with (A4J.AJAX) {" +
" var queues = A4J.AJAX.EventQueue.getQueues();" +
" for (var queueNames in queues) {" +
" return A4J.AJAX.EventQueue.getQueue(queueNames).getSize() <= 0;" +
" }" +
" }" +
" }" +
" return true;";
try {
return (Boolean) ((JavascriptExecutor) driver).executeScript(jScript);
} catch (WebDriverException e) {
return true;
}
}
public String toString() {
return "active queues to finish.";
}
};
If you use jquery, you could try this instead for letting current ajax work finish:
public ExpectedCondition<Boolean> allAjaxRequestsFinish() {
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
try {
return (Boolean) ((JavascriptExecutor) driver).executeScript("return jQuery.active == 0");
} catch (Exception e) {
return true;
}
}
public String toString() {
return "all ajax requests to finish.";
}
};
}
All these conditions or any condition you create in this template can be used for dynamic waiting in that same way:
wait.until(activeQueuesToFinish());
wait.until(allAjaxRequestsFinish());
Edit:
The "template" I mean is a function with this form:
public ExpectedCondition<Boolean> myCustomCondition(/* some arguments from the outside */) {
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
// Check something on the page and return either true or false,
// this method will be run repeatedly until either the time
// limit is exceeded, or true is returned
}
public String toString() {
return "a description of what this is waiting for";
}
};
}
Here's an example of how you could do this to wait on a specific element that may be holding up your page:
public ExpectedCondition<Boolean> waitForElementToHaveText(final WebElement element, final String expectedText) {
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
try {
return element.getText().equals(expectedText);
} catch (Exception e) {
return false; // catchall fail case
}
}
public String toString() {
return "an element to have specific text";
}
};
}
来源:https://stackoverflow.com/questions/49595601/selenium-java-safaridriver-wait-for-page-to-load-after-click