Some Selenium tests wait for each page to be loaded fully before interacting with it as follows:
@Test
public void searchReturnsResultsTest() {
HomePage homePage = new HomePage(driver);
homePage.open();
homePage.waitUntilLoaded();
Assert.assertTrue(homePage.isDisplayed(),
"home page is not displayed!");
ResultsPage resultsPage = homePage.searchBy(KEYWORD);
resultsPage.waitUntilLoaded();
Assert.assertTrue(resultsPage.isDisplayed(),
"results page is not displayed!");
assertContains(resultPage.getPaginationInfo(),
"1 to 10",
"pagination info does not include 1 to 10!");
}
The test opens home page, waits for home page to be fully loaded and then interacts with it. Later, results page is opened as a consequence of the keyword search. The test again waits for the results page to be fully loaded and then interacts with it.
Why does the test wait for a page to be fully loaded before interacting with it?
The idea is that, if the page is fully loaded, then all its elements are in the DOM so they can be interacted with immediately. There is no need of synchronization in this case and the probability of getting element exceptions is very low.
How is this “page load” wait method implemented?
public void waitUntilLoaded() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until((ExpectedCondition<Boolean>) d ->
((JavascriptExecutor) d).executeScript(
"return document.readyState").equals("complete"));
}
The method waits until the document.readyState value is equal to complete.
The document.readyState property describes the loading state of the document in the browser. This property is not part of Selenium but of Javascript. This is why the method needs the JavascriptExecutor to execute this Javascript code.
The readyState value of a document can be one of following:
loading - the document is still loading
interactive - the document has finished loading and the document has been parsed but sub-resources such as scripts, images, stylesheets and frames are still loading.
complete - The document and all sub-resources have finished loading
There is no indication anywhere that, when document.readyState = complete, all elements of the page are in the DOM. This is not what happens in the browser.
The problem, obviously, are page redirects via AJAX requests and running scripts as those can't be caught by Selenium. Also, you can't reliably catch them via readyState as it will signal complete long before all the AJAX content is downloaded.
So, this method will never tell us that all page elements are loaded because it cannot catch the state of all elements.
Unfortunately, there is no general solution for waiting until the page is fully loaded.
If this does not work, what else can we do?
How about not waiting for the page to be fully loaded?
Why should we do this?
Is this a good idea or a bad one?
Our test interacts with only a few elements of the page.
It does not interact with all page's elements.
Waiting for the full page to be loaded means waiting for elements that are needed but also for elements that are not needed. This seems like a waste of time, in my opinion.
Instead of waiting for the page to be loaded, we can use a different approach:
wait until the correct page is displayed in the browser by checking the page url
wait until the first element needed by the test is available
interact with the first element
wait until the second element needed by the test is available
interact with the second element
continue in this manner until the test ends
We do not need this method:
public void waitUntilLoaded() {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until((ExpectedCondition<Boolean>) d ->
((JavascriptExecutor) d).executeScript(
"return document.readyState").equals("complete"));
}
Instead, use this method to wait until the page url is correct:
public void waitUntilUrlContains(String url) {
wait(Duration.ofSeconds(30)).until(ExpectedConditions.urlContains(url));
}
You can use this method in the isDisplayed() method of any page class:
public boolean isDisplayed() {
waitUntilUrlContains(PAGE_URL);
return true;
}
isDisplayed() generates an exception if the page url is incorrect. Otherwise, it returns true;
After the page url is correct, we can wait until each element is available using one of the following 3 methods:
public WebElement findElement(By by) {
WebElement element = wait(Duration.ofSeconds(30)).until(
ExpectedConditions.presenceOfElementLocatedBy(by));
return element;
}
public WebElement findVisibleElement(By by) {
WebElement element = wait(Duration.ofSeconds(30)).until(
ExpectedConditions.visibilityOfElementLocated(by));
return element;
}
public WebElement findClickableElement(By by) {
WebElement element = wait(Duration.ofSeconds(30)).until(
ExpectedConditions.elementToBeClickable(by));
return element;
}
private WebDriverWait wait (Duration duration) {
return new WebDriverWait(driver, duration);
}
These 3 methods can be used in all your page methods. The first can be used when you just need the element to be present in the DOM, the second for visible elements, the third for clickable elements.
Waiting for the url of the page to be correct and waiting for each element to be present (or visible or clickable) before interacting with it will work well so there is no need to wait for the page to load completely.