Page Load Strategy and Timeouts
Control when driver.get() returns with normal, eager and none strategies, set the three session timeouts (page load, script, implicit), and combine them with explicit waits for fast, stable tests.
Every navigation blocks until the browser says the page has loaded. What “loaded” means is configurable, and picking the right definition is the difference between a suite that waits 30 seconds for a third-party analytics script and one that moves on the moment your app’s HTML is parsed.
The Three Page Load Strategies
| Strategy | get() returns when | Typical use |
|---|---|---|
normal (default) | The load event fires: HTML plus all subresources (images, CSS, scripts, iframes) | Traditional server-rendered pages |
eager | DOMContentLoaded fires: HTML parsed, DOM built; subresources may still be loading | Single-page apps and pages with slow third-party assets |
none | Immediately after the navigation starts | Full control with explicit waits; performance-sensitive suites |
With eager or none you must wait explicitly for the element you need. That is good practice anyway.
import org.openqa.selenium.PageLoadStrategy;
ChromeOptions options = new ChromeOptions();options.setPageLoadStrategy(PageLoadStrategy.EAGER); // NORMAL, EAGER, NONEWebDriver driver = new ChromeDriver(options);
driver.get("https://app.example.com"); // returns at DOMContentLoadednew WebDriverWait(driver, Duration.ofSeconds(10)) .until(ExpectedConditions.visibilityOfElementLocated(By.id("app-ready")));options = webdriver.ChromeOptions()options.page_load_strategy = "eager" # "normal", "eager", "none"driver = webdriver.Chrome(options=options)
driver.get("https://app.example.com")WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "app-ready")))const chrome = require('selenium-webdriver/chrome');
const options = new chrome.Options().setPageLoadStrategy('eager');const driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build();
await driver.get('https://app.example.com');await driver.wait(until.elementIsVisible(driver.findElement(By.id('app-ready'))), 10000);var options = new ChromeOptions { PageLoadStrategy = PageLoadStrategy.Eager };var driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://app.example.com");new WebDriverWait(driver, TimeSpan.FromSeconds(10)) .Until(d => d.FindElement(By.Id("app-ready")).Displayed);The same option works on FirefoxOptions, EdgeOptions and SafariOptions; it is a W3C capability (pageLoadStrategy), not a Chrome extension.
When Each Strategy Makes Sense
Stick with normal for pages whose content depends on images or scripts finishing (visual tests, PDF generation) and when you are not sure. It is the safest default.
Choose eager for single-page apps. The framework’s JavaScript is usually loaded by DOMContentLoaded; everything after that (API calls, lazy images) needs an explicit wait regardless of strategy. Suites with heavy third-party assets (chat widgets, analytics, fonts) often cut minutes off their run time with this one change.
Choose none when you want to interact with a page while it is still loading, or when a page never fires load (some long-polling dashboards). Every interaction then needs an explicit wait, and your wait timeouts become the effective page-load timeout.
The Three Session Timeouts
Independently of the strategy, every session has three timeouts:
| Timeout | Default | Applies to |
|---|---|---|
| Page load | 300 s | get(), navigate().to(), back(), forward(), refresh() |
| Script | 30 s | executeAsyncScript() |
| Implicit | 0 s | Every findElement / findElements retry window |
A page load that exceeds the timeout raises TimeoutException; the navigation is aborted. Set it lower than the default: a page that takes five minutes is a failed test, not a slow one.
// At runtimedriver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(15));driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0)); // keep it at 0
// Or in options so every session starts configuredChromeOptions options = new ChromeOptions();options.setPageLoadTimeout(Duration.ofSeconds(30));options.setScriptTimeout(Duration.ofSeconds(15));options.setImplicitWaitTimeout(Duration.ofSeconds(0));
// Read back what the driver hasSystem.out.println(driver.manage().timeouts().getPageLoadTimeout());# At runtimedriver.set_page_load_timeout(30)driver.set_script_timeout(15)driver.implicitly_wait(0)
# Or in options (milliseconds)options = webdriver.ChromeOptions()options.timeouts = {"pageLoad": 30_000, "script": 15_000, "implicit": 0}
# Read backprint(driver.timeouts.page_load)// At runtime (milliseconds)await driver.manage().setTimeouts({ pageLoad: 30000, script: 15000, implicit: 0 });
// Read backconst timeouts = await driver.manage().getTimeouts();console.log(timeouts.pageLoad);// At runtimedriver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(30);driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(15);driver.Manage().Timeouts().ImplicitWait = TimeSpan.Zero;Handling a Page Load Timeout Gracefully
Sometimes the page is usable even though load never fires because one tracking pixel hangs. Catch the timeout, stop the load, and continue with explicit waits.
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(15));try { driver.get("https://app.example.com/report");} catch (TimeoutException e) { ((JavascriptExecutor) driver).executeScript("window.stop();"); // abort remaining requests}// Continue as long as what we need is presentnew WebDriverWait(driver, Duration.ofSeconds(10)) .until(ExpectedConditions.presenceOfElementLocated(By.id("report-table")));driver.set_page_load_timeout(15)try: driver.get("https://app.example.com/report")except TimeoutException: driver.execute_script("window.stop();")
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "report-table")))await driver.manage().setTimeouts({ pageLoad: 15000 });try {await driver.get('https://app.example.com/report');} catch (e) {if (e.name !== 'TimeoutError') throw e;await driver.executeScript('window.stop();');}await driver.wait(until.elementLocated(By.id('report-table')), 10000);driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(15);try{ driver.Navigate().GoToUrl("https://app.example.com/report");}catch (WebDriverTimeoutException){ ((IJavaScriptExecutor)driver).ExecuteScript("window.stop();");}new WebDriverWait(driver, TimeSpan.FromSeconds(10)) .Until(d => d.FindElements(By.Id("report-table")).Count > 0);Waiting for “Ready” in a Single-Page App
document.readyState === 'complete' is the JavaScript equivalent of the load event and tells you nothing about pending API calls. Better signals, in order:
- A specific element that only renders after data arrives. This is the best signal in almost every case.
- An app-provided flag such as
window.__APP_READY__or adata-loadedattribute on the root; ask developers for one. - Network idleness via BiDi: count
network.beforeRequestSentandnetwork.responseCompletedevents and wait until they balance. See Custom Wait Conditions. - Framework hooks: Angular exposes
getAllAngularTestabilities(); jQuery apps exposejQuery.active === 0. Only for legacy apps.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
// Best: the thing you needwait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("[data-testid='orders-table'] tbody tr")));
// Fallback: DOM finished parsing and subresources loadedwait.until(d -> "complete".equals(((JavascriptExecutor) d).executeScript("return document.readyState")));wait = WebDriverWait(driver, 15)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='orders-table'] tbody tr")))
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")await driver.wait(until.elementLocated(By.css("[data-testid='orders-table'] tbody tr")), 15000);
await driver.wait(async () => (await driver.executeScript('return document.readyState')) === 'complete', 15000);var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
wait.Until(d => d.FindElements(By.CssSelector("[data-testid='orders-table'] tbody tr")).Any(e => e.Displayed));
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));A Recommended Configuration
For a modern web app under test, this combination is fast and predictable:
pageLoadStrategy = eagerpageLoadTimeout = 30 s(or lower, matching your performance budget)implicitWait = 0- Explicit waits of 10 s on every interaction that follows a navigation or an API call
- One shared
WebDriverWaitper page object, not per test
Summary
normalwaits for everything,eagerfor the DOM,nonefor nothing; paireagerandnonewith explicit waits.- Set a realistic page load timeout and handle the exception by stopping the load if the page is usable.
readyStateis not “app ready.” Wait for the element you need, or a flag the app exposes.- Keep implicit wait at zero so explicit waits behave predictably.