• Home
  • /
  • Blog
  • /
  • Selenium Wait for Page to Load in Python With Code Examples (2026)
Selenium PythonAutomationTutorial

Selenium Wait for Page to Load in Python With Code Examples (2026)

Struggling with NoSuchElementException errors? Learn Selenium wait for page load strategies like implicit, explicit, and fluent waits for stable tests.

Author

Saniya Gazala

April 13, 2026

If your Selenium Python script is throwing NoSuchElementException or ElementNotInteractableException, the root cause is almost always a missing or incorrect wait, your script moved to the next command before the page finished loading. This guide walks through every wait strategy in Python with working code, a live demo on both local and cloud execution, and a decision guide so you know exactly which approach to use for each scenario.

Overview

Why Use Selenium Wait for Page to Load?

Using Selenium wait for page to load ensures that dynamic elements on a page are fully loaded and available for interaction. This is crucial for avoiding errors like ElementNotVisibleException during automated tests, especially when dealing with dynamic content and AJAX calls.

  • Handling Dynamic Content: Selenium wait helps automate interactions with elements that load dynamically (e.g., during file uploads or delayed confirmations).
  • Prevents Errors: Avoids exceptions when elements are not ready for interaction, ensuring smoother automation tests.
  • Improves Test Stability: Ensures elements are present and interactable, reducing test flakiness and improving reliability.

What Are the Various Types of Selenium Waits?

Selenium provides different wait strategies to handle dynamic web elements and ensure page content is fully loaded before interacting with them:

  • Explicit Wait: Waits for a specific condition to be met before continuing with the test (e.g., an element becomes clickable).
  • Implicit Wait: Applies a default waiting time throughout the test, allowing Selenium to poll the DOM until the element is found.
  • Fluent Wait: Similar to explicit wait but offers more control over polling frequency and the handling of exceptions.

How to Use Implicit Wait?

Implicit waits are set globally, allowing you to specify how long Selenium should wait for an element to appear in the DOM. This method is applied throughout the session, making it useful for tests where elements may take varying amounts of time to load:

  • Usage Example: driver.implicitly_wait(10) waits up to 10 seconds for elements to appear before throwing a NoSuchElementException.

How to Use Explicit Wait?

Explicit waits allow you to pause the execution of your script until a certain condition is met (e.g., visibility of an element). The WebDriverWait class, combined with expected conditions, is used to implement this:

  • Usage Example: WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "element_id"))) waits for the element to be present for up to 10 seconds before proceeding.

How to Use Fluent Wait?

Fluent wait is an advanced version of explicit wait, providing more control over polling frequency and exception handling. It is useful when you need to wait for elements that might intermittently appear or change state:

  • Usage Example: WebDriverWait(driver, 10, poll_frequency=2).until(EC.presence_of_element_located((By.ID, "element_id"))) waits for the element while checking every 2 seconds.

SmartWait in TestMu AI

SmartWait is a feature in TestMu AI that enhances the efficiency of Selenium wait strategies. It performs actionability checks on webpage elements before executing actions, ensuring that all conditions are met and minimizing errors in automated tests:

  • Usage Example: Set the smartWait time limit in the configuration to ensure elements are ready before actions are performed.
  • LT:Options {...
    "smartWait": 10 // It accepts integer values as second
    ...
    } 

What Is Selenium Wait for Page to Load?

Selenium wait for page to load pauses test script execution until a page or DOM element is ready, preventing exceptions from elements that haven't loaded yet.

Selenium wait for page to load is a mechanism that pauses your test script's execution until a page or a specific DOM element is ready for interaction. Without a wait, Selenium executes the next command immediately after triggering a navigation or action, meaning it may try to find or click an element that does not exist in the DOM yet.

Modern web applications load content asynchronously using JavaScript and AJAX. The initial HTML arrives from the server, but components like form fields, buttons, modals, and data tables populate after additional network requests complete. Selenium has no built-in awareness of these asynchronous events unless you explicitly instruct it to wait.

When Selenium tries to interact with an element before it has loaded, it raises one of these exceptions:

  • NoSuchElementException: Element not present in the DOM
  • ElementNotInteractableException: Element exists in the DOM but is hidden or disabled
  • StaleElementReferenceException: Element was found, but the DOM refreshed before the interaction completed

The above are the common Selenium exceptions that you encounter every now and then while running Selenium automation; however, Selenium waits prevent all three by holding execution until a specified condition evaluates to true.

Austin Siewert

Austin Siewert

CEO, Vercel

Discovered @TestMu AI yesterday. Best browser testing tool I've found for my use case. Great pricing model for the limited testing I do 👏

2M+ Devs and QAs rely on TestMu AI

Deliver immersive digital experiences with Next-Generation Mobile Apps and Cross Browser Testing Cloud

This certification is for professionals looking to develop advanced, hands-on expertise in Selenium automation testing with Python and take their career to the next level.

Here's a short glimpse of the Selenium Python 101 certification from TestMu AI:

How Selenium Decides a Page Is "Loaded": Page Load Strategy?

Selenium uses the page load strategy to map document.readyState: normal waits for complete, eager waits for interactive, and none returns control immediately.

Before adding waits to your scripts, it is important to understand how Selenium itself determines when a page is ready. This is controlled by the page load strategy, which maps to the browser's document.readyState property defined in the WHATWG HTML Living Standard.

The document.readyState property can hold three values:

  • loading: The document is still being fetched.
  • interactive: The document has been parsed; sub-resources like images and stylesheets may still be loading.
  • complete: The document and all sub-resources have finished loading. Selenium considers the page fully loaded at this stage.

Selenium maps these to three page load strategies:

StrategyreadyState Waited ForWhat It Means
normal (default)completeFull page load: HTML, CSS, JS, images, and iframes are completely loaded.
eagerinteractiveHTML is parsed, and the DOM is ready, but sub-resources like images and styles may still be loading.
none(no wait)WebDriver returns control immediately after the navigation request without waiting for the page to load.

By default, every driver.get() or driver.navigate() call blocks until document.readyState == "complete". Understanding this mechanism is the foundation of implementing the right Selenium wait for page to load strategy in the test scripts.

Setting Page Load Strategy in Python:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.page_load_strategy = 'eager'  # 'normal', 'eager', or 'none'

driver = webdriver.Chrome(options=options)
driver.get('https://example.com')
# Output: Driver returns control once DOM is interactive,
# without waiting for images or stylesheets to finish loading

Manually checking the document.readyState via JavaScript:

Even with normal strategy, dynamically injected elements can appear after readyState reaches complete. You can poll readyState directly using a JavaScript executor:

from selenium.webdriver.support.ui import WebDriverWait

WebDriverWait(driver, 30).until(
    lambda d: d.execute_script('return document.readyState') == 'complete'
)
# Output: Wait resolves as soon as readyState equals 'complete'
# TimeoutException is raised if 30 seconds elapse without resolution

The official Selenium waiting strategies documentation covers the underlying WebDriver spec for each strategy in detail.

What Are the Different Types of Selenium Waits in Python?

Selenium Python offers three wait types: Implicit Wait (global DOM polling), Explicit Wait (condition-specific), and Fluent Wait (custom polling intervals and exception handling).

Selenium Python provides three wait mechanisms for implementing a Selenium wait for page to load in your test scripts, each designed for a different synchronization scenario:

Wait TypeHow It WorksBest For
Implicit WaitGlobally polls the DOM for a set duration when trying to find elements.Simple pages with predictable and consistent load times.
Explicit WaitWaits for a specific condition to occur for a particular element before proceeding.Dynamic elements, AJAX content, or elements that appear based on conditions.
Fluent WaitAn explicit wait with customizable polling intervals and the ability to ignore specific exceptions.Intermittently appearing elements and slow-loading widgets.

Important: Mixing implicit and explicit waits in the same session can cause unpredictable total wait durations. The Selenium Python documentation recommends choosing one approach and using it consistently.

Implicit Wait in Selenium Python

Implicit wait sets a global timeout for all find_element calls in the WebDriver session. If an element is not found immediately, Selenium keeps polling the DOM until it appears or the timeout expires.

driver.implicitly_wait(10)
# Applies globally for the entire session
# Every find_element call will retry for up to 10 seconds before raising NoSuchElementException

For the full parameter reference, DOM polling behavior, and when to avoid implicit wait.

Explicit Wait in Selenium Python

Explicit wait pauses execution until a specific condition is met for a specific element. It uses WebDriverWait combined with ExpectedConditions.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By


element = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.ID, 'submit-btn'))
)
# Output: Returns the WebElement once it is visible in the DOM
# TimeoutException is raised if 10 seconds pass without the element becoming visible

For the full ExpectedConditions reference and advanced chaining patterns. To use these waits to their full potential, follow this guide on Implicit and Explicit Wait in Selenium.

Fluent Wait in Selenium Python

Fluent wait is an explicit wait with two additional controls: poll_frequency (how often Selenium re-checks the condition) and ignored_exceptions (which exceptions to suppress while polling).

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException


element = WebDriverWait(
    driver,
    timeout=10,
    poll_frequency=2,
    ignored_exceptions=[NoSuchElementException]
).until(
    EC.presence_of_element_located((By.ID, 'dynamic-element'))
)
# Output: Polls the DOM every 2 seconds instead of the default 500ms
# NoSuchElementException is suppressed between polls — no early failure
# TimeoutException is raised only after the full 10-second window expires

Use fluent wait when an element loads intermittently or when the default 500ms polling creates noise in your logs.

How to Implement Selenium Waits for Page to Load?

Use implicitly_wait() for global session waits, WebDriverWait with ExpectedConditions for element-specific waits, or Fluent Wait with custom polling intervals and ignored exceptions.

This section demonstrates how all three wait types behave against a real dynamic page, with execution running on the TestMu AI (formerly LambdaTest) Selenium Grid cloud platfrom.

You will see the exact timestamps and output for each wait type. Once you review these demos, the next section explains how to scale the same tests using HyperExecute and how SmartWait adds a browser-level safety layer on top of your existing wait strategies.

The Demo Page

The following HTML creates a page with two dynamic behaviors: a button that appears 3 seconds after clicking "Try it", and an alert that fires 2 seconds after that button loads. This simulates the kind of delayed, conditional element loading common in real applications.

<!DOCTYPE html>
<html>
<body>
  <p>Click the button below. A new button will appear after 3 seconds.</p>
  <button onclick="setTimeout(myFunction, 3000);">Try it</button>

  <script>
    function myFunction() {
      var btn = document.createElement("BUTTON");
      btn.innerHTML = "CLICK ME";
      btn.id = "waitCreate";
      document.body.appendChild(btn);
      setTimeout(function () {
        alert("Created 2 seconds after CLICK ME appeared.");
      }, 2000);
    }
  </script>
</body>
</html>
github

When the page loads, you see only the "Try it" button. Clicking it triggers:

  • A "CLICK ME" button appears after 3 seconds.
  • An alert firing 2 seconds after the CLICK ME button appears.

Without waits, any attempt to interact with CLICK ME immediately after clicking "Try it" will raise NoSuchElementException because the element does not exist in the DOM yet.

Demo 1: Implicit Wait

Below is the code implementation for demonstrating implicit wait on the local ecosystem.

Example

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
import time
from datetime import datetime
from os import environ
 
class DragTest(unittest.TestCase):
    def setUp(self):
        # configuration to test in the cloud using lambdaTest
        username = environ.get('LT_USERNAME', None)
        accessToken = environ.get('LT_ACCESS_KEY', None)
        gridUrl = "hub.lambdatest.com/wd/hub"
        ch_options = webdriver.ChromeOptions()

        lt_options = {}
        lt_options["build"] = "Build: LambdaTest python selenium implicit wait testing automation"
        lt_options["project"] = "Project: LambdaTest python selenium implicit wait testing automation"
        lt_options["name"] = "Test: LambdaTest python selenium implicit wait testing automation"

        lt_options["browserName"] = "Chrome"
        lt_options["browserVersion"] = "latest"
        lt_options["platformName"] = "Windows 11"

        lt_options["console"] = "error"
        lt_options["w3c"] = True,
        lt_options["headless"] = False
        lt_options["network"] = True,
        lt_options["video"] = True,
        lt_options["visual"] = True

        ch_options.set_capability('LT:Options', lt_options)

        url = "https://"+username+":"+accessToken+"@"+gridUrl
     
        self.driver = webdriver.Remote(
            command_executor = url,
            options = ch_options
        )
 
    def test_selenium_implicit_wait(self):
        driver = self.driver
        driver.maximize_window()
        # defining condition for implicit waits - we have set 10 seconds
        driver.implicitly_wait(10)
        driver.get('https://pynishant.github.io/Selenium-python-waits.html')
        pageLoadedClock = datetime.now()
        current_time_after_page_loaded = pageLoadedClock.strftime("%H:%M:%S")
        print("Time after page load and before clicking the Try it button=", current_time_after_page_loaded)
        driver.find_element(By.XPATH, '//button[text()="Try it"]').click()
        # this won't FAIL with implicit time set
        try:
            # printing time to demonstrate waits
            pageLoadClock = datetime.now()
            current_time = pageLoadClock.strftime("%H:%M:%S")
            print("Time before starting polling for CLICK ME Button =", current_time)
            driver.find_element(By.XPATH, '//button[text()="CLICK ME"]').click()
            pageLoadClock = datetime.now()
            current_time = pageLoadClock.strftime("%H:%M:%S")
            print("Time after CLICK ME was found =", current_time)
        except Exception as e:
            print(e)
 
    def tearDown(self):
        # closes the driver
        self.driver.quit()
 
if __name__ == '__main__':
    unittest.main()

Output:

...implicit wait selenium

Result:

execution of implicit wait

The 3-second gap between the second and third timestamps confirms Selenium was holding execution via implicit wait, polling the DOM until CLICK ME appeared. No exception was raised because the element appeared within the 10-second window.

Demo 2: Explicit Wait

Below is the code implementation for demonstrating explicit wait on the local ecosystem.

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
import time
from datetime import datetime
from os import environ
 
class DragTest(unittest.TestCase):
    def setUp(self):
        # configuration to test in the cloud using lambdaTest
        username = environ.get('LT_USERNAME', None)
        accessToken = environ.get('LT_ACCESS_KEY', None)
        gridUrl = "hub.lambdatest.com/wd/hub"

        ch_options = webdriver.ChromeOptions()

        lt_options = {}
        lt_options["build"] = "Build: LambdaTest python selenium explicit wait testing automation"
        lt_options["project"] = "Project: LambdaTest python selenium explicit wait testing automation"
        lt_options["name"] = "Test: LambdaTest python selenium explicit wait testing automation"

        lt_options["browserName"] = "Chrome"
        lt_options["browserVersion"] = "latest"
        lt_options["platformName"] = "Windows 11"

        lt_options["console"] = "error"
        lt_options["w3c"] = True,
        lt_options["headless"] = False
        lt_options["network"] = True,
        lt_options["video"] = True,
        lt_options["visual"] = True

        ch_options.set_capability('LT:Options', lt_options)

        url = "https://"+username+":"+accessToken+"@"+gridUrl
     
        self.driver = webdriver.Remote(
            command_executor = url,
            options = ch_options
        )
 
    def test_selenium_explicit_wait(self):
        driver = self.driver
        driver.maximize_window()

        # printing time to demonstrate waits
        pageLoadClock = datetime.now()
        current_time = pageLoadClock.strftime("%H:%M:%S")
        print("Time before starting page load =", current_time)
        driver.get('https://pynishant.github.io/Selenium-python-waits.html')
        pageLoadedClock = datetime.now()
        current_time_after_page_loaded = pageLoadedClock.strftime("%H:%M:%S")
        print("Time after page load and before clicking the Try it button=", current_time_after_page_loaded)
        driver.find_element(By.XPATH, '//button[text()="Try it"]').click()
        # this is scripted to FAIL
        try:
            driver.find_element(By.XPATH, '//button[text()="CLICK ME"]').click()
        except Exception as e:
            ExceptionClock = datetime.now()
            current_time_Click_me_failed = ExceptionClock.strftime("%H:%M:%S")
            print("
Time when click me was attempted to interact with but failed=", current_time_Click_me_failed)
            print("The below exception occurred because we didn't wait for the element 'button' to be available before interaction.")
            print(e)
           
        # Explicit Wait Demo
        try:
            WebDriverClock = datetime.now()
            current_time_webdriver = WebDriverClock.strftime("%H:%M:%S")
            print("
Time before waiting for CLICK ME button with webdriver=", current_time_webdriver)
            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "waitCreate")))
            ButtonFound = datetime.now()
            current_time_ButtonFound = ButtonFound.strftime("%H:%M:%S")
            print("Time when Button Found=", current_time_ButtonFound)
            print("This was interacting with Button using Explicit wait. Now we shall interact with alert using fluent wait.")
        except Exception as e:
            print(e)
            print("error in try block")
 
    def tearDown(self):
        # closes the driver
        self.driver.quit()
 
if __name__ == '__main__':
    unittest.main()

Output

explicit wait

Result:

execution of explicit wait

The first attempt fails immediately because no wait is applied. The explicit wait then holds for ~3 seconds until the element appears, demonstrating the difference between an unwaited interaction and a properly synchronized one.

Demo 3: Fluent Wait

Below is the code implementation for demonstrating fluent wait on the local ecosystem.

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
import time
from datetime import datetime
from os import environ
 
class DragTest(unittest.TestCase):
    def setUp(self):
        # configuration to test in the cloud using lambdaTest
        username = environ.get('LT_USERNAME', None)
        accessToken = environ.get('LT_ACCESS_KEY', None)
        gridUrl = "hub.lambdatest.com/wd/hub"

        ch_options = webdriver.ChromeOptions()

        lt_options = {}
        lt_options["build"] = "Build: LambdaTest python selenium fluent wait testing automation"
        lt_options["project"] = "Project: LambdaTest python selenium fluent wait testing automation"
        lt_options["name"] = "Test: LambdaTest python selenium fluent wait testing automation"

        lt_options["browserName"] = "Chrome"
        lt_options["browserVersion"] = "latest"
        lt_options["platformName"] = "Windows 11"

        lt_options["console"] = "error"
        lt_options["w3c"] = True,
        lt_options["headless"] = False
        lt_options["network"] = True,
        lt_options["video"] = True,
        lt_options["visual"] = True
        ch_options.set_capability('LT:Options', lt_options)

        url = "https://"+username+":"+accessToken+"@"+gridUrl
     
        self.driver = webdriver.Remote(
            command_executor = url,
            options = ch_options
        )
 
    def test_selenium_fluent_wait(self):
        driver = self.driver
        driver.maximize_window()

        # printing time to demonstrate waits
        pageLoadClock = datetime.now()
        current_time = pageLoadClock.strftime("%H:%M:%S")
        print("Time before starting page load =", current_time)
        driver.get('https://pynishant.github.io/Selenium-python-waits.html')
        pageLoadedClock = datetime.now()
        current_time_after_page_loaded = pageLoadedClock.strftime("%H:%M:%S")
        print("Time after page load and before clicking the Try it button=", current_time_after_page_loaded)
        driver.find_element(By.XPATH, '//button[text()="Try it"]').click()
        # this is scripted to FAIL
        try:
            driver.find_element(By.XPATH, '//button[text()="CLICK ME"]').click()
        except Exception as e:
            ExceptionClock = datetime.now()
            current_time_Click_me_failed = ExceptionClock.strftime("%H:%M:%S")
            print("
Time when click me was attempted to interact with but failed=", current_time_Click_me_failed)
            print("The below exception occurred because we didn't wait for the element 'button' to be available before interaction.")
            print(e)
           
        try:
            alertClock = datetime.now()
            current_time_alertClock = alertClock.strftime("%H:%M:%S")
            print("
Time before waiting for alert with webdriver=", current_time_alertClock)
            WebDriverWait(driver, 7, poll_frequency=5).until(EC.alert_is_present(), 'Timed out waiting for simple alert to appear')
            alertFound = datetime.now()
            current_time_alertFound = alertFound.strftime("%H:%M:%S")
            print("Time when Button Found=", current_time_alertFound)
            alert = driver.switch_to.alert
            time.sleep(1)
            alert.accept()
        except Exception as e:
            print(e)
 
    def tearDown(self):
        # closes the driver
        self.driver.quit()
 
if __name__ == '__main__':
    unittest.main()

Output

fluent wait

Result

execution of fluent wait

The fluent wait polls every 5 seconds (not the default 500ms) and finds the alert at ~5 seconds, which is when the 3-second button delay plus the 2-second alert delay resolves. The higher poll frequency avoids hammering the DOM while still catching the condition within the timeout window.

Note

Note: Increase efficiency & accuracy of automated test execution with Selenium Waits. Try TestMu AI Today!

Enhance Selenium Waits with SmartWait on TestMu AI

WebDriverWait confirms that an element meets a condition, visible, present, clickable, but it does not verify whether the element is genuinely ready to receive the action at the browser level. An element can pass element_to_be_clickable and still fail the actual click if it is mid-animation, partially obscured by an overlay, or not yet in the viewport on a slower machine.

SmartWait is a TestMu AI(formerly LambdaTest) platform feature that runs a series of actionability checks on an element immediately before the action executes, independent of which wait strategy your test code uses. It works alongside your existing WebDriverWait or implicitly_wait setup, not as a replacement for them.

To enable SmartWait, add it to the LT:Options capability block in the same setUp() used in the demos above:

Why Waits Behave Inconsistently in CI/CD?

In CI/CD, waits behave inconsistently due to network latency between the test runner, browser, and Selenium grid, causing timeouts that never occur on local machines.

In CI/CD pipelines or cross-browser testing across different operating systems, wait timeouts can behave inconsistently. For example, a WebDriverWait(driver, 10) that consistently resolves in about 3 seconds on a local machine may take up to 9–10 seconds or even timeout when executed on a shared Selenium grid due to network latency between the test runner, browser, and automation infrastructure.

How HyperExecute Improves Wait Reliability?

HyperExecute improves wait reliability by co-locating test scripts and browser components in isolated JIT VMs, eliminating network latency and making timeouts consistent.

To overcome these inconsistencies, cloud-based platforms like TestMu AI (formerly LambdaTest) provide optimized execution environments. Its HyperExecute feature accelerates test runs and stabilizes wait behavior.

HyperExecute solves the wait inconsistency problem by running Selenium tests online in isolated Just-in-Time (JIT) virtual machines, where the test scripts and browser components execute within the same environment. This setup eliminates network hops between services, allowing wait conditions to resolve faster and more consistently while significantly speeding up overall test execution.

    Key capabilities:

  • 70% Faster Execution: Isolated, unified environments place test scripts and all browser components together, matching local execution speeds, which means your WebDriverWait timeouts reflect actual application behavior, not infrastructure noise.
  • Flaky Test Detection: Automatically identifies tests that fail inconsistently due to timing issues, helping you distinguish genuine wait failures from execution environment instability.
  • Automatic Test Retries: Configurable retry logic handles transient timeout failures without manual intervention, giving your wait strategies a fair chance before marking a test as permanently failed.
  • Unified Logging and Debugging: Terminal logs, Selenium logs, network logs, and video recordings are collected separately per test, so when a wait times out, you can inspect exactly what the page state was at the moment of failure.

Running Selenium Tests on HyperExecute

Below is the YAML configuration to execute Selenium Python wait tests on HyperExecute:

version: 0.1
globalTimeout: 90
testSuiteTimeout: 90
testSuiteStep: 90

runson: win
autosplit: true
retryOnFailure: true
maxRetries: 1

concurrency: 5

env:
  LT_USERNAME: ${{ .secrets.LT_USERNAME }}
  LT_ACCESS_KEY: ${{ .secrets.LT_ACCESS_KEY }}

pre:
  - pip install selenium --break-system-packages

testDiscovery:
  type: raw
  mode: static
  command: grep -nri 'def test_' tests/ | awk '{print $1}' | sed 's/:.*//'

testRunnerCommand: python -m pytest -s $test

Why This Configuration Works

  • autosplit: true: Distributes test files automatically across available parallel slots.
  • retryOnFailure: true: Retries any test that fails due to a timeout, giving your wait strategies a second chance to succeed.
  • testDiscovery: Uses a custom command to find all test functions, ensuring that each test is executed as an individual unit for better isolation and reporting.

Each test run produces separate Selenium logs, a video recording, and a network log accessible directly from the HyperExecute dashboard, making wait timeout diagnosis immediate rather than a guessing exercise.

To get started with HyperExecute, follow this support documentation on how to run your first job on HyperExecute .

...

How to Wait for Page Load After a Click?

After a click, wait for a specific element on the new page, use staleness_of() to detect DOM replacement, or poll document.readyState after a brief 500ms startup pause.

Clicking a link or button that triggers full page navigation is one of the trickiest scenarios for Selenium wait for page to load, after a click, Selenium does not automatically know whether the browser navigated to a new page, triggered an AJAX update, or fired a JavaScript event with no navigation.

Three approaches handle post-click page loads reliably.

Approach 1: Wait for a Specific Element on the New Page

The most reliable method. After clicking, wait for an element that only exists on the destination page.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By


driver.find_element(By.LINK_TEXT, 'Checkout').click()


WebDriverWait(driver, 15).until(
    EC.presence_of_element_located((By.ID, 'order-confirmation'))
)
# Output: Execution resumes the moment the order-confirmation element appears in the DOM
# TimeoutException is raised if the new page does not load within 15 seconds

Approach 2: Use staleness_of to Detect Page Replacement

When navigation loads a completely new page, the old page's DOM elements become stale. staleness_of waits until a reference element from the old page is detached from the DOM, confirming the new page has replaced it.

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By


old_page_element = driver.find_element(By.TAG_NAME, 'html')
driver.find_element(By.LINK_TEXT, 'Next Page').click()


WebDriverWait(driver, 15).until(
    EC.staleness_of(old_page_element)
)
# Output: Wait resolves the moment the old HTML element is detached from the DOM
# Particularly useful for paginated tables and multi-step forms

Approach 3: Poll document.readyState After a Click

For clicks that trigger full page reloads, polling readyState after a brief startup pause handles most cases:

import time
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By


driver.find_element(By.ID, 'submit').click()
time.sleep(0.5)  # Allow navigation to start before polling


WebDriverWait(driver, 20).until(
    lambda d: d.execute_script('return document.readyState') == 'complete'
)
# Output: Wait resolves once readyState reaches 'complete' on the new page
# The 0.5s sleep prevents polling the readyState of the old page before navigation starts

The 0.5 second sleep before polling is intentional. Without it, Selenium may check readyState on the old page before the browser has started processing the click, a race condition that readyState polling alone cannot resolve.

How to Wait for AJAX Requests to Complete?

Wait for a loading spinner to disappear with invisibility_of_element_located, wait for the target element directly, or use the Performance API via JavaScript to check all requests completed.

The most complex scenario for Selenium wait for page to load is Single Page Applications and AJAX-heavy pages, where document.readyState reaches complete long before the content you test actually needs is available.

The DOM is technically "loaded", but data tables, form fields, and widgets are still being populated by asynchronous network requests.

Strategy 1: Wait for a Loading Indicator to Disappear

Most SPAs display a spinner or progress bar while data is being fetched. Wait for it to disappear before interacting with the page content.

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By


WebDriverWait(driver, 20).until(
    EC.invisibility_of_element_located((By.CLASS_NAME, 'loading-spinner'))
)
# Output: Execution resumes the moment the spinner is no longer visible in the DOM

Strategy 2: Wait Directly for the Target Element

Skip the spinner and wait for the element your test actually needs. This is more precise and less brittle than waiting for a loading indicator.

WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, 'table.results-table'))
)
# Output: Returns the table element once it is present and visible in the DOM

Strategy 3: Check Network Idle via the Performance API

For pages where no visible loading indicator exists, use the browser's Performance API via JavaScript to confirm all pending network requests have completed:

def network_idle(driver):
    script = """
    return window.performance.getEntriesByType('resource')
        .filter(r => !r.responseEnd).length === 0;
    """
    return driver.execute_script(script)


WebDriverWait(driver, 30).until(network_idle)
# Output: Resolves once all resource entries in the Performance API have a responseEnd value
# Meaning: no network requests are still in flight

set_page_load_timeout()and set_script_timeou()

Beyond wait strategies, Selenium provides two session-level timeout methods that cap how long WebDriver will block for specific operations.

set_page_load_timeout(seconds)

Sets the maximum time Selenium will wait for a page to fully load. If the page does not reach readyState == 'complete' within this window, TimeoutException is raised.

driver.set_page_load_timeout(30)
driver.get('https://heavy-page.example.com')
# Output: TimeoutException is raised if the page does not fully load within 30 seconds
# Useful for testing under throttled network conditions or with heavy third-party scripts

set_script_timeout(seconds)

Sets the maximum time for execute_async_script() to complete before raising TimeoutException.

driver.set_script_timeout(15)
driver.execute_async_script("""
    var callback = arguments[arguments.length - 1];
    setTimeout(callback, 5000);
""")
# Output: Script completes after ~5 seconds; TimeoutException if it exceeds 15 seconds

Both timeouts apply to the entire session. Set them in setUp() or during driver initialization, not mid-test, to keep session configuration predictable.

Common Selenium Wait Errors and How to Fix Them

During Selenium automation, wait-related errors often occur when elements load dynamically or the DOM updates before the test interacts with them.

The following errors occur specifically when a Selenium wait for the page to load is missing, wrongly configured, or uses the wrong Expected Conditions in Selenium for the situation.

ExceptionRoot CauseFix
NoSuchElementExceptionElement not in the DOM when Selenium looked for it.Add an implicit wait or use presence_of_element_located with WebDriverWait.
ElementNotInteractableExceptionElement exists in the DOM but is hidden or disabled.Use visibility_of_element_located or element_to_be_clickable instead of presence_of_element_located.
StaleElementReferenceExceptionDOM refreshed after the element reference was captured.Re-locate the element inside the wait condition instead of storing the reference before performing an action.
TimeoutExceptionThe expected condition was not met within the timeout period.Verify the locator is correct, increase the timeout, and ensure the element actually appears on the page.
ElementClickInterceptedExceptionAnother element (like a modal or cookie banner) is covering the target element.Wait for the overlay to disappear using invisibility_of_element_located before clicking the target element.

Which Selenium Wait Should You Use?

Use explicit WebDriverWait for most scenarios, implicit wait for simple pages, staleness_of after page navigation, and Performance API for AJAX and SPA content.

Choosing the correct Selenium wait for page to load strategy depends on what your test is waiting for, whether it's for full DOM load, a specific element, an AJAX response, or page replacement after a click.

Below is the recommended approach that you can choose when you fall into certain scenarios.

ScenarioRecommended Approach
Simple pages, consistent load timesimplicitly_wait() set once at session start
Wait for a specific element to appearWebDriverWait + EC.presence_of_element_located
Wait for element to be visible and clickableWebDriverWait+ EC.element_to_be_clickable
Wait for full DOM completion WebDriverWait + lambda d: d.execute_script('return document.readyState') == 'complete'
Wait after clicking a link to a new pageWebDriverWait + EC.staleness_of(old_element)
AJAX or SPA content loading after DOM readyWebDriverWait + EC.visibility_of_element_located on target or EC.invisibility_of_element_located on spinner
Element appears intermittently or very slowlyFluent wait with poll_frequency=2 or poll_frequency=3
Pending network requestsJavaScript + Performance API + WebDriverWait
Heavy pages in throttled environmentsset_page_load_timeout() with an appropriate buffer

The default rule: use explicit wait. It is precise, predictable, and scoped to what your test actually needs. Use implicit wait only as a global safety net on stable, simple page structures. Never use time.sleep() in production test code, the only acceptable exception is a 200-500ms pause immediately after a click to let navigation begin before you start pollingreadyState.

To learn more about Selenium waits, including how to configure a Selenium wait for page load strategy, explore other core Selenium features, and understand how to get started with test automation, follow this Selenium tutorial.

Conclusion

Selenium's wait for page to load is not a single method; it is a toolkit matched to specific scenarios. Use page_load_strategy and set_page_load_timeout to control navigation-level behavior. .

Use WebDriverWait with the right ExpectedCondition for element synchronization. Use staleness_of after link clicks. Use JavaScript-based checks for AJAX and SPA content. And validate your wait strategies against real browser and network conditions on HyperExecute to separate actual application timing from infrastructure noise.

Happy Testing!

Author

Saniya Gazala is a Product Marketing Manager and Community Evangelist at TestMu AI with 2+ years of experience in software QA, manual testing, and automation adoption. She holds a B.Tech in Computer Science Engineering. At TestMu AI, she leads content strategy, community growth, and test automation initiatives, having managed a 5-member team and contributed to certification programs using Selenium, Cypress, Playwright, Appium, and KaneAI. Saniya has authored 15+ articles on QA and holds certifications in Automation Testing, Six Sigma Yellow Belt, Microsoft Power BI, and multiple automation tools. She also crafted hands-on problem statements for Appium and Espresso. Her work blends detailed execution with a strategic focus on impact, learning, and long-term community value.

Open in ChatGPT Icon

Open in ChatGPT

Open in Claude Icon

Open in Claude

Open in Perplexity Icon

Open in Perplexity

Open in Grok Icon

Open in Grok

Open in Gemini AI Icon

Open in Gemini AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it freeArrowArrow
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for freeArrowArrow

Frequently asked questions

Did you find this page helpful?

More Related Hubs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests