Python bindings for Selenium WebDriver providing automated browser control for multiple browsers including Chrome, Firefox, Edge, Safari, and Internet Explorer
—
This document covers WebDriverWait and Expected Conditions in Python Selenium WebDriver, which provide mechanisms for handling dynamic web content by waiting for specific conditions to be met before proceeding with test execution.
{ .api }
from selenium.webdriver.support.ui import WebDriverWait
class WebDriverWait(Generic[D]):
def __init__(
self,
driver: Union[WebDriver, WebElement],
timeout: float,
poll_frequency: float = 0.5,
ignored_exceptions: Optional[WaitExcTypes] = None,
)Description: WebDriverWait implements explicit waits that pause execution until a certain condition is met or a timeout occurs.
Parameters:
driver: Instance of WebDriver (Chrome, Firefox, etc.) or a WebElementtimeout: Number of seconds before timing outpoll_frequency: Sleep interval between calls (default: 0.5 seconds)ignored_exceptions: Iterable of exception classes to ignore during calls (default: NoSuchElementException)Default Values:
POLL_FREQUENCY: 0.5 secondsIGNORED_EXCEPTIONS: (NoSuchElementException,)Example:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# Basic wait setup
wait = WebDriverWait(driver, 10)
# Wait with custom poll frequency
fast_wait = WebDriverWait(driver, 5, poll_frequency=0.1)
# Wait with custom ignored exceptions
from selenium.common.exceptions import ElementNotVisibleException
custom_wait = WebDriverWait(
driver,
10,
ignored_exceptions=(ElementNotVisibleException, NoSuchElementException)
){ .api }
def until(
self,
method: Callable[[D], Union[Literal[False], T]],
message: str = ""
) -> TDescription: Waits until the method returns a value that is not False.
Parameters:
method: A callable that takes a WebDriver instance and returns a truthy value when the condition is metmessage: Optional message for TimeoutExceptionReturns: The result of the last call to method
Raises:
TimeoutException: If 'method' does not return a truthy value within the timeoutExample:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Wait for element to be visible
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.visibility_of_element_located((By.ID, "myElement"))
)
# Wait with custom message
element = wait.until(
EC.element_to_be_clickable((By.ID, "submit-btn")),
"Submit button did not become clickable"
){ .api }
def until_not(
self,
method: Callable[[D], T],
message: str = ""
) -> Union[T, Literal[True]]Description: Waits until the method returns a value that evaluates to False.
Parameters:
method: A callable that takes a WebDriver instance as an argumentmessage: Optional message for TimeoutExceptionReturns: The result of the last call to method or True if an ignored exception occurred
Raises:
TimeoutException: If 'method' does not return False within the timeoutExample:
# Wait for element to disappear
wait = WebDriverWait(driver, 10)
is_disappeared = wait.until_not(
EC.visibility_of_element_located((By.ID, "loading-spinner"))
)
# Wait for element to become stale
old_element = driver.find_element(By.ID, "dynamic-content")
# Trigger page refresh or DOM change
refresh_button.click()
wait.until_not(EC.staleness_of(old_element))Expected Conditions provide pre-built condition functions for common waiting scenarios.
from selenium.webdriver.support import expected_conditions as EC{ .api }
def title_is(title: str) -> Callable[[WebDriver], bool]Description: An expectation for checking the title of a page.
Parameters:
title: The expected title (exact match)Returns: True if the title matches, False otherwise
{ .api }
def title_contains(title: str) -> Callable[[WebDriver], bool]Description: An expectation for checking that the title contains a case-sensitive substring.
Parameters:
title: The fragment of title expectedReturns: True when the title contains the substring, False otherwise
Example:
# Wait for exact title
wait.until(EC.title_is("Welcome to My Site"))
# Wait for title to contain text
wait.until(EC.title_contains("Dashboard")){ .api }
def url_contains(url: str) -> Callable[[WebDriver], bool]Description: An expectation for checking that the current URL contains a case-sensitive substring.
Parameters:
url: The fragment of URL expectedReturns: True when the URL contains the substring, False otherwise
{ .api }
def url_matches(pattern: str) -> Callable[[WebDriver], bool]Description: An expectation for checking that the current URL matches a regular expression pattern.
Parameters:
pattern: The regex pattern to matchReturns: True when the URL matches the pattern, False otherwise
{ .api }
def url_to_be(url: str) -> Callable[[WebDriver], bool]Description: An expectation for checking the current URL.
Parameters:
url: The expected URL (exact match)Returns: True if the URL matches, False otherwise
{ .api }
def url_changes(url: str) -> Callable[[WebDriver], bool]Description: An expectation for checking that the current URL does not match the given URL.
Parameters:
url: The URL to compare againstReturns: True if current URL is different, False otherwise
Example:
# Wait for URL to contain specific path
wait.until(EC.url_contains("/dashboard"))
# Wait for URL to match pattern
wait.until(EC.url_matches(r"https://.*\.example\.com/user/\d+"))
# Wait for navigation to complete
current_url = driver.current_url
login_button.click()
wait.until(EC.url_changes(current_url)){ .api }
def presence_of_element_located(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], WebElement]Description: An expectation for checking that an element is present on the DOM. This does not necessarily mean that the element is visible.
Parameters:
locator: Used to find the element (By strategy, value)Returns: The WebElement once it is located
{ .api }
def presence_of_all_elements_located(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], List[WebElement]]Description: An expectation for checking that there is at least one element present on a web page.
Parameters:
locator: Used to find the elementsReturns: List of WebElements once they are located
Example:
# Wait for single element to be present in DOM
element = wait.until(
EC.presence_of_element_located((By.ID, "content"))
)
# Wait for multiple elements to be present
elements = wait.until(
EC.presence_of_all_elements_located((By.CLASS_NAME, "item"))
){ .api }
def visibility_of_element_located(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], WebElement]Description: An expectation for checking that an element is present on the DOM and visible. Visibility means that the element is not only displayed but also has a height and width greater than 0.
Parameters:
locator: Used to find the elementReturns: The WebElement once it is located and visible
{ .api }
def visibility_of(element: WebElement) -> Callable[[Any], Union[Literal[False], WebElement]]Description: An expectation for checking that an element, known to be present on the DOM, is visible.
Parameters:
element: The WebElement to checkReturns: The WebElement if visible, False otherwise
{ .api }
def visibility_of_all_elements_located(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], List[WebElement]]Description: An expectation for checking that all elements are present on the DOM and visible.
Parameters:
locator: Used to find the elementsReturns: List of WebElements once they are located and visible
{ .api }
def visibility_of_any_elements_located(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], List[WebElement]]Description: An expectation for checking that there is at least one element visible on a web page.
Parameters:
locator: Used to find the elementsReturns: List of visible WebElements
Example:
# Wait for element to be visible
visible_element = wait.until(
EC.visibility_of_element_located((By.ID, "modal"))
)
# Wait for existing element to become visible
element = driver.find_element(By.ID, "hidden-content")
visible_element = wait.until(EC.visibility_of(element))
# Wait for all matching elements to be visible
all_visible = wait.until(
EC.visibility_of_all_elements_located((By.CLASS_NAME, "menu-item"))
)
# Wait for any matching elements to be visible
any_visible = wait.until(
EC.visibility_of_any_elements_located((By.TAG_NAME, "button"))
){ .api }
def invisibility_of_element_located(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], Union[WebElement, bool]]Description: An expectation for checking that an element is either invisible or not present on the DOM.
Parameters:
locator: Used to find the elementReturns: WebElement if invisible, True if not present
{ .api }
def invisibility_of_element(
element: WebElement
) -> Callable[[Any], Union[WebElement, bool]]Description: An expectation for checking that an element is either invisible or not present on the DOM.
Parameters:
element: The WebElement to checkReturns: WebElement if invisible, True if not present
Example:
# Wait for loading spinner to disappear
wait.until(
EC.invisibility_of_element_located((By.ID, "loading-spinner"))
)
# Wait for specific element to become invisible
modal = driver.find_element(By.ID, "error-modal")
close_button.click()
wait.until(EC.invisibility_of_element(modal)){ .api }
def element_to_be_clickable(
mark: Union[WebElement, Tuple[str, str]]
) -> Callable[[WebDriverOrWebElement], Union[Literal[False], WebElement]]Description: An expectation for checking an element is visible and enabled such that you can click it.
Parameters:
mark: WebElement or locator tuple (By strategy, value)Returns: The WebElement once it is clickable, False otherwise
Example:
# Wait for button to be clickable
clickable_button = wait.until(
EC.element_to_be_clickable((By.ID, "submit-btn"))
)
clickable_button.click()
# Wait for existing element to be clickable
button = driver.find_element(By.ID, "action-btn")
wait.until(EC.element_to_be_clickable(button)){ .api }
def element_to_be_selected(element: WebElement) -> Callable[[Any], bool]Description: An expectation for checking that an element is selected.
Parameters:
element: The WebElement to checkReturns: True if the element is selected, False otherwise
{ .api }
def element_located_to_be_selected(
locator: Tuple[str, str]
) -> Callable[[WebDriverOrWebElement], bool]Description: An expectation for the element to be located and selected.
Parameters:
locator: Used to find the elementReturns: True if the element is selected, False otherwise
{ .api }
def element_selection_state_to_be(
element: WebElement,
is_selected: bool
) -> Callable[[Any], bool]Description: An expectation for checking if the given element's selection state matches the expected state.
Parameters:
element: The WebElement to checkis_selected: Expected selection stateReturns: True if the element's selection state matches, False otherwise
{ .api }
def element_located_selection_state_to_be(
locator: Tuple[str, str],
is_selected: bool
) -> Callable[[WebDriverOrWebElement], bool]Description: An expectation to locate an element and check if its selection state matches the expected state.
Parameters:
locator: Used to find the elementis_selected: Expected selection stateReturns: True if the element's selection state matches, False otherwise
Example:
# Wait for checkbox to be selected
checkbox = driver.find_element(By.ID, "agree-terms")
wait.until(EC.element_to_be_selected(checkbox))
# Wait for element to be found and selected
wait.until(
EC.element_located_to_be_selected((By.ID, "default-option"))
)
# Wait for specific selection state
wait.until(
EC.element_selection_state_to_be(checkbox, True)
)
# Wait for located element to have specific selection state
wait.until(
EC.element_located_selection_state_to_be((By.ID, "toggle"), False)
){ .api }
def text_to_be_present_in_element(
locator: Tuple[str, str],
text_: str
) -> Callable[[WebDriverOrWebElement], bool]Description: An expectation for checking if the given text is present in the specified element.
Parameters:
locator: Used to find the elementtext_: The text to check forReturns: True if text is present, False otherwise
{ .api }
def text_to_be_present_in_element_value(
locator: Tuple[str, str],
text_: str
) -> Callable[[WebDriverOrWebElement], bool]Description: An expectation for checking if the given text is present in the element's value attribute.
Parameters:
locator: Used to find the elementtext_: The text to check for in the value attributeReturns: True if text is present in value, False otherwise
{ .api }
def text_to_be_present_in_element_attribute(
locator: Tuple[str, str],
attribute_: str,
text_: str
) -> Callable[[WebDriverOrWebElement], bool]Description: An expectation for checking if the given text is present in the element's specified attribute.
Parameters:
locator: Used to find the elementattribute_: The attribute to checktext_: The text to check for in the attributeReturns: True if text is present in attribute, False otherwise
Example:
# Wait for text to appear in element
wait.until(
EC.text_to_be_present_in_element(
(By.ID, "status"), "Operation completed"
)
)
# Wait for text in input value
wait.until(
EC.text_to_be_present_in_element_value(
(By.ID, "search-box"), "selenium"
)
)
# Wait for text in custom attribute
wait.until(
EC.text_to_be_present_in_element_attribute(
(By.ID, "progress"), "data-status", "finished"
)
){ .api }
def element_attribute_to_include(
locator: Tuple[str, str],
attribute_: str
) -> Callable[[WebDriverOrWebElement], bool]Description: An expectation for checking if the element has a particular attribute.
Parameters:
locator: Used to find the elementattribute_: The attribute to check forReturns: True if the attribute is present, False otherwise
Example:
# Wait for element to have specific attribute
wait.until(
EC.element_attribute_to_include(
(By.ID, "upload-btn"), "disabled"
)
){ .api }
def staleness_of(element: WebElement) -> Callable[[Any], bool]Description: Wait until an element is no longer attached to the DOM.
Parameters:
element: The element to wait forReturns: False if the element is still attached, True otherwise
Example:
# Store reference to element before DOM change
old_element = driver.find_element(By.ID, "dynamic-content")
# Trigger page refresh or dynamic update
refresh_button.click()
# Wait for old element to become stale
wait.until(EC.staleness_of(old_element))
# Now safe to find the new element
new_element = driver.find_element(By.ID, "dynamic-content"){ .api }
def frame_to_be_available_and_switch_to_it(
locator: Union[Tuple[str, str], str]
) -> Callable[[WebDriver], bool]Description: An expectation for checking whether the given frame is available to switch to. If the frame is available, it switches to it.
Parameters:
locator: Either a frame name/id (string) or a locator tupleReturns: True if the frame was switched to, False otherwise
Example:
# Wait for frame to be available and switch to it
wait.until(
EC.frame_to_be_available_and_switch_to_it("payment-frame")
)
# Or using locator
wait.until(
EC.frame_to_be_available_and_switch_to_it(
(By.ID, "checkout-iframe")
)
)
# Interact with frame content
frame_button = driver.find_element(By.ID, "pay-now")
frame_button.click()
# Switch back to default content
driver.switch_to.default_content(){ .api }
def number_of_windows_to_be(num_windows: int) -> Callable[[WebDriver], bool]Description: An expectation for the number of windows to be a certain value.
Parameters:
num_windows: The expected number of windowsReturns: True when the number of windows matches, False otherwise
{ .api }
def new_window_is_opened(current_handles: List[str]) -> Callable[[WebDriver], bool]Description: An expectation that a new window will be opened and have the number of windows handles increase.
Parameters:
current_handles: List of current window handlesReturns: True when a new window is opened, False otherwise
Example:
# Store current window handles
current_handles = driver.window_handles
# Click link that opens new window
external_link.click()
# Wait for new window to open
wait.until(EC.new_window_is_opened(current_handles))
# Switch to new window
new_handles = driver.window_handles
new_window = [h for h in new_handles if h not in current_handles][0]
driver.switch_to.window(new_window)
# Wait for specific number of windows
wait.until(EC.number_of_windows_to_be(3)){ .api }
def alert_is_present() -> Callable[[WebDriver], Union[Alert, Literal[False]]]Description: An expectation for checking that an alert is present.
Returns: Alert object if present, False otherwise
Example:
# Trigger alert
delete_button.click()
# Wait for alert and handle it
alert = wait.until(EC.alert_is_present())
alert_text = alert.text
alert.accept() # or alert.dismiss(){ .api }
def any_of(*expected_conditions: Callable[[D], T]) -> Callable[[D], Union[Literal[False], T]]Description: An expectation that any of multiple expected conditions is true. Equivalent to a logical OR.
Parameters:
*expected_conditions: Variable number of expected condition functionsReturns: The result of the first condition that evaluates to True, False if none do
Example:
# Wait for either success or error message
result = wait.until(
EC.any_of(
EC.visibility_of_element_located((By.ID, "success-message")),
EC.visibility_of_element_located((By.ID, "error-message"))
)
)
# Check which condition was met
if result.get_attribute("id") == "success-message":
print("Operation succeeded")
else:
print("Operation failed")While not built into Selenium, you can create an all_of condition:
def all_of(*expected_conditions):
"""Wait for all conditions to be true"""
def _predicate(driver):
results = []
for condition in expected_conditions:
result = condition(driver)
if not result:
return False
results.append(result)
return results
return _predicate
# Usage
wait.until(
all_of(
EC.visibility_of_element_located((By.ID, "form")),
EC.element_to_be_clickable((By.ID, "submit")),
EC.text_to_be_present_in_element((By.ID, "status"), "Ready")
)
)You can create custom expected conditions by following the same pattern:
def element_has_css_class(locator, css_class):
"""Wait for element to have a specific CSS class"""
def _predicate(driver):
try:
element = driver.find_element(*locator)
classes = element.get_attribute("class")
return css_class in classes.split() if classes else False
except:
return False
return _predicate
def page_source_contains(text):
"""Wait for page source to contain specific text"""
def _predicate(driver):
return text in driver.page_source
return _predicate
def element_count_to_be(locator, count):
"""Wait for specific number of elements"""
def _predicate(driver):
elements = driver.find_elements(*locator)
return len(elements) == count
return _predicate
# Usage
wait.until(element_has_css_class((By.ID, "status"), "active"))
wait.until(page_source_contains("Welcome"))
wait.until(element_count_to_be((By.CLASS_NAME, "item"), 5))def wait_for_form_ready(driver):
"""Wait for form to be completely ready for interaction"""
wait = WebDriverWait(driver, 10)
# Wait for form to be visible
form = wait.until(
EC.visibility_of_element_located((By.ID, "registration-form"))
)
# Wait for all required fields to be present
wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, "input[required]"))
)
# Wait for submit button to be clickable
wait.until(
EC.element_to_be_clickable((By.ID, "submit-btn"))
)
return formdef wait_for_search_results(driver, search_term):
"""Wait for search results to load and contain expected content"""
wait = WebDriverWait(driver, 15)
# Wait for loading to disappear
wait.until(
EC.invisibility_of_element_located((By.ID, "search-loading"))
)
# Wait for results container to be visible
results_container = wait.until(
EC.visibility_of_element_located((By.ID, "search-results"))
)
# Wait for at least one result or no-results message
wait.until(
EC.any_of(
EC.presence_of_element_located((By.CLASS_NAME, "result-item")),
EC.text_to_be_present_in_element(
(By.ID, "search-results"), "No results found"
)
)
)
return results_containerdef safe_wait_and_click(driver, locator, timeout=10):
"""Safely wait for element and click with error handling"""
try:
wait = WebDriverWait(driver, timeout)
element = wait.until(EC.element_to_be_clickable(locator))
element.click()
return True
except TimeoutException:
print(f"Element {locator} not clickable within {timeout} seconds")
return False
except Exception as e:
print(f"Error clicking element {locator}: {e}")
return False
# Usage
if safe_wait_and_click(driver, (By.ID, "submit-btn")):
print("Successfully clicked submit button")
else:
print("Failed to click submit button")def wait_for_api_response(driver, timeout=30):
"""Wait for API response to be displayed"""
wait = WebDriverWait(driver, timeout, poll_frequency=0.5)
def api_response_ready(driver):
try:
status = driver.find_element(By.ID, "api-status")
response = driver.find_element(By.ID, "api-response")
# Check if API call is complete
if status.text == "Complete":
# Check if response has content
if response.text and response.text != "Loading...":
return {"status": status.text, "response": response.text}
return False
except:
return False
return wait.until(api_response_ready)
# Usage
result = wait_for_api_response(driver)
print(f"API Status: {result['status']}")
print(f"Response: {result['response']}")from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
class FluentWait:
def __init__(self, driver, timeout=10, poll_frequency=0.5):
self.driver = driver
self.timeout = timeout
self.poll_frequency = poll_frequency
self.ignored_exceptions = []
def ignoring(self, *exceptions):
"""Add exceptions to ignore"""
self.ignored_exceptions.extend(exceptions)
return self
def until(self, condition, message=None):
"""Wait until condition is met"""
wait = WebDriverWait(
self.driver,
self.timeout,
self.poll_frequency,
tuple(self.ignored_exceptions) if self.ignored_exceptions else None
)
return wait.until(condition, message)
# Usage
from selenium.common.exceptions import StaleElementReferenceException
result = FluentWait(driver, timeout=15, poll_frequency=0.2)\
.ignoring(StaleElementReferenceException)\
.until(EC.element_to_be_clickable((By.ID, "dynamic-btn")))This comprehensive guide covers all aspects of WebDriverWait and Expected Conditions, providing you with the tools to handle any dynamic web content scenario in your Selenium automation.
Install with Tessl CLI
npx tessl i tessl/pypi-selenium