Fluent Waits
Configure advanced wait behavior with polling intervals, ignored exceptions, and custom messages.
Selenium 3 & 4 Stable
Fluent waits provide the most control over wait behavior, allowing you to configure polling frequency, which exceptions to ignore, and custom timeout messages.
Basic Fluent Wait
Configuring Fluent Wait
Selenium 3 & 4 Stable
import org.openqa.selenium.support.ui.FluentWait;import org.openqa.selenium.NoSuchElementException;import java.time.Duration;import java.util.function.Function;
// Create fluent wait with custom configurationFluentWait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(30)) // Maximum wait time .pollingEvery(Duration.ofMillis(500)) // Check every 500ms .ignoring(NoSuchElementException.class); // Ignore this exception
// Use the waitWebElement element = wait.until(driver -> { return driver.findElement(By.id("dynamic-content"));});
// With custom message for debuggingFluentWait<WebDriver> waitWithMessage = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(30)) .pollingEvery(Duration.ofMillis(500)) .ignoring(NoSuchElementException.class) .withMessage("Waiting for dynamic content to load");from selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.common.exceptions import NoSuchElementException
# WebDriverWait in Python is already a fluent wait# Configure with additional parameterswait = WebDriverWait( driver, timeout=30, # Maximum wait time poll_frequency=0.5, # Check every 500ms ignored_exceptions=[NoSuchElementException])
# Use the waitelement = wait.until( EC.presence_of_element_located((By.ID, "dynamic-content")))
# Custom condition with lambdaelement = wait.until( lambda d: d.find_element(By.ID, "dynamic-content"))// JavaScript uses the standard WebDriver wait with conditions// Configure timeout and polling through the wait call
const { until } = require('selenium-webdriver');
// Basic fluent wait patternasync function fluentWait(driver, locator, timeout = 30000, pollInterval = 500) { const endTime = Date.now() + timeout;
while (Date.now() < endTime) { try { const element = await driver.findElement(locator); if (await element.isDisplayed()) { return element; } } catch (e) { // Ignore and retry } await driver.sleep(pollInterval); } throw new Error(`Element not found within ${timeout}ms`);}
// Usageconst element = await fluentWait(driver, By.id('dynamic-content'));
// Or use built-in wait with custom conditionconst element = await driver.wait(async () => { try { return await driver.findElement(By.id('dynamic-content')); } catch (e) { return null; }}, 30000);using OpenQA.Selenium.Support.UI;using System;
// Create fluent wait with custom configurationDefaultWait<IWebDriver> wait = new DefaultWait<IWebDriver>(driver){ Timeout = TimeSpan.FromSeconds(30), // Maximum wait time PollingInterval = TimeSpan.FromMilliseconds(500), // Check every 500ms Message = "Waiting for dynamic content to load"};wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
// Use the waitIWebElement element = wait.Until(d => { return d.FindElement(By.Id("dynamic-content"));});
// WebDriverWait also supports fluent configurationWebDriverWait webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)){ PollingInterval = TimeSpan.FromMilliseconds(500), Message = "Custom timeout message"};webDriverWait.IgnoreExceptionTypes(typeof(NoSuchElementException));Polling Intervals
Configure how frequently the condition is checked:
Custom Polling Intervals
Selenium 3 & 4 Stable
// Fast polling for quick elementsFluentWait<WebDriver> fastWait = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(5)) .pollingEvery(Duration.ofMillis(100)); // Check every 100ms
// Slow polling for resources/networkFluentWait<WebDriver> slowWait = new FluentWait<>(driver) .withTimeout(Duration.ofMinutes(2)) .pollingEvery(Duration.ofSeconds(2)); // Check every 2 seconds
// Adaptive polling examplepublic WebElement waitWithAdaptivePolling(By locator) { // Start with fast polling, increase if taking longer for (int pollMs : new int[]{100, 250, 500, 1000}) { FluentWait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(5)) .pollingEvery(Duration.ofMillis(pollMs)) .ignoring(NoSuchElementException.class);
try { return wait.until(d -> d.findElement(locator)); } catch (TimeoutException e) { // Try with longer polling } } throw new TimeoutException("Element not found: " + locator);}# Fast polling for quick elementsfast_wait = WebDriverWait( driver, timeout=5, poll_frequency=0.1 # Check every 100ms)
# Slow polling for resources/networkslow_wait = WebDriverWait( driver, timeout=120, poll_frequency=2 # Check every 2 seconds)
# Adaptive polling exampledef wait_with_adaptive_polling(driver, locator): """Start with fast polling, increase if taking longer""" for poll_sec in [0.1, 0.25, 0.5, 1.0]: wait = WebDriverWait( driver, timeout=5, poll_frequency=poll_sec, ignored_exceptions=[NoSuchElementException] ) try: return wait.until(EC.presence_of_element_located(locator)) except TimeoutException: pass # Try with longer polling
raise TimeoutException(f"Element not found: {locator}")// Fast polling for quick elementsasync function fastWait(driver, locator) { return customFluentWait(driver, locator, 5000, 100);}
// Slow polling for resources/networkasync function slowWait(driver, locator) { return customFluentWait(driver, locator, 120000, 2000);}
// Helper function for custom pollingasync function customFluentWait(driver, locator, timeout, pollInterval) { const endTime = Date.now() + timeout;
while (Date.now() < endTime) { try { const element = await driver.findElement(locator); return element; } catch (e) { await driver.sleep(pollInterval); } } throw new Error('Element not found');}// Fast polling for quick elementsvar fastWait = new DefaultWait<IWebDriver>(driver){ Timeout = TimeSpan.FromSeconds(5), PollingInterval = TimeSpan.FromMilliseconds(100) // Check every 100ms};
// Slow polling for resources/networkvar slowWait = new DefaultWait<IWebDriver>(driver){ Timeout = TimeSpan.FromMinutes(2), PollingInterval = TimeSpan.FromSeconds(2) // Check every 2 seconds};Ignoring Exceptions
Specify which exceptions to ignore during polling:
Ignoring Exceptions
Selenium 3 & 4 Stable
import org.openqa.selenium.NoSuchElementException;import org.openqa.selenium.StaleElementReferenceException;import org.openqa.selenium.ElementNotInteractableException;
FluentWait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(30)) .pollingEvery(Duration.ofMillis(500)) // Ignore multiple exceptions .ignoring(NoSuchElementException.class) .ignoring(StaleElementReferenceException.class) .ignoring(ElementNotInteractableException.class);
// Or add all at onceFluentWait<WebDriver> wait2 = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(30)) .pollingEvery(Duration.ofMillis(500)) .ignoreAll(Arrays.asList( NoSuchElementException.class, StaleElementReferenceException.class ));
// Use case: Click element that might be staleWebElement button = wait.until(d -> { WebElement el = d.findElement(By.id("submit")); el.click(); // This line can throw if element becomes stale return el;});from selenium.common.exceptions import ( NoSuchElementException, StaleElementReferenceException, ElementNotInteractableException)
wait = WebDriverWait( driver, timeout=30, poll_frequency=0.5, ignored_exceptions=[ NoSuchElementException, StaleElementReferenceException, ElementNotInteractableException ])
# Use case: Click element that might be staledef click_when_ready(driver): element = driver.find_element(By.ID, "submit") element.click() return element
button = wait.until(click_when_ready)// Custom wait that ignores specific errorsasync function waitIgnoringErrors(driver, condition, timeout = 30000) { const endTime = Date.now() + timeout;
while (Date.now() < endTime) { try { const result = await condition(); if (result) return result; } catch (e) { // Check if it's an ignorable error const ignorable = [ 'NoSuchElementError', 'StaleElementReferenceError', 'ElementNotInteractableError' ]; if (!ignorable.some(err => e.name.includes(err))) { throw e; // Re-throw unexpected errors } } await driver.sleep(500); } throw new Error('Condition not met within timeout');}using OpenQA.Selenium;
var wait = new DefaultWait<IWebDriver>(driver){ Timeout = TimeSpan.FromSeconds(30), PollingInterval = TimeSpan.FromMilliseconds(500)};
// Ignore multiple exceptionswait.IgnoreExceptionTypes( typeof(NoSuchElementException), typeof(StaleElementReferenceException), typeof(ElementNotInteractableException));
// Use case: Click element that might be staleIWebElement button = wait.Until(d => { IWebElement el = d.FindElement(By.Id("submit")); el.Click(); return el;});Custom Wait Conditions
Create reusable wait conditions:
Custom Wait Functions
Selenium 3 & 4 Stable
import java.util.function.Function;
// Custom condition: Element has specific textFunction<WebDriver, WebElement> elementHasText(By locator, String text) { return driver -> { WebElement element = driver.findElement(locator); if (element.getText().contains(text)) { return element; } return null; };}
// Custom condition: Element countFunction<WebDriver, List<WebElement>> elementsCountAtLeast(By locator, int count) { return driver -> { List<WebElement> elements = driver.findElements(locator); if (elements.size() >= count) { return elements; } return null; };}
// Custom condition: Page fully loadedFunction<WebDriver, Boolean> pageLoadComplete() { return driver -> { return ((JavascriptExecutor) driver) .executeScript("return document.readyState") .equals("complete"); };}
// UsageFluentWait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(30)) .pollingEvery(Duration.ofMillis(500));
WebElement element = wait.until(elementHasText(By.id("status"), "Complete"));List<WebElement> items = wait.until(elementsCountAtLeast(By.className("item"), 5));wait.until(pageLoadComplete());# Custom condition: Element has specific textdef element_has_text(locator, text): def condition(driver): try: element = driver.find_element(*locator) if text in element.text: return element except: pass return False return condition
# Custom condition: Element countdef elements_count_at_least(locator, count): def condition(driver): elements = driver.find_elements(*locator) if len(elements) >= count: return elements return False return condition
# Custom condition: Page fully loadeddef page_load_complete(driver): return driver.execute_script("return document.readyState") == "complete"
# Usagewait = WebDriverWait(driver, 30, poll_frequency=0.5)
element = wait.until(element_has_text((By.ID, "status"), "Complete"))items = wait.until(elements_count_at_least((By.CLASS_NAME, "item"), 5))wait.until(page_load_complete)// Custom condition: Element has specific textfunction elementHasText(locator, text) { return async (driver) => { try { const element = await driver.findElement(locator); const elementText = await element.getText(); return elementText.includes(text) ? element : null; } catch (e) { return null; } };}
// Custom condition: Element countfunction elementsCountAtLeast(locator, count) { return async (driver) => { const elements = await driver.findElements(locator); return elements.length >= count ? elements : null; };}
// Custom condition: Page fully loadedasync function pageLoadComplete(driver) { const state = await driver.executeScript('return document.readyState'); return state === 'complete';}
// Usageconst element = await driver.wait( elementHasText(By.id('status'), 'Complete'), 30000);// Custom condition: Element has specific textFunc<IWebDriver, IWebElement> ElementHasText(By locator, string text){ return driver => { try { IWebElement element = driver.FindElement(locator); return element.Text.Contains(text) ? element : null; } catch { return null; } };}
// Custom condition: Element countFunc<IWebDriver, IList<IWebElement>> ElementsCountAtLeast(By locator, int count){ return driver => { IList<IWebElement> elements = driver.FindElements(locator); return elements.Count >= count ? elements : null; };}
// Custom condition: Page fully loadedFunc<IWebDriver, bool> PageLoadComplete(){ return driver => { return ((IJavaScriptExecutor)driver) .ExecuteScript("return document.readyState") .Equals("complete"); };}
// Usagevar wait = new DefaultWait<IWebDriver>(driver){ Timeout = TimeSpan.FromSeconds(30), PollingInterval = TimeSpan.FromMilliseconds(500)};
IWebElement element = wait.Until(ElementHasText(By.Id("status"), "Complete"));Fluent Wait vs WebDriverWait
| Feature | WebDriverWait | FluentWait |
|---|---|---|
| Default polling | 500ms | Configurable |
| Exception handling | NoSuchElement only | Any exceptions |
| Custom messages | No | Yes |
| Generic input | WebDriver only | Any object |
| Use case | Standard waits | Custom scenarios |
Best Practices
- Tune polling interval based on the expected element behavior
- Ignore appropriate exceptions but not all exceptions
- Add meaningful messages for debugging failures
- Create reusable conditions for common patterns
- Consider performance - shorter polling = more CPU usage
Next Steps
- Expected Conditions - Built-in wait conditions reference
- Explicit Waits - Simpler wait configuration
- Common Errors - Fix TimeoutException and other issues
- Page Object Model - Integrate waits into page objects