Published on

Updated

Essential Selenium Extensions in C#

Essential Selenium extension methods

Selenium WebDriver helps automate browser tests. These extension methods make common waits easier to read while keeping failures visible.

Originally published in July 2022; examples updated on 3 October 2026.

Setup and scope

The examples use Selenium.WebDriver and Selenium.Support 4.49.0. They use lambda conditions, so DotNetSeleniumExtras.WaitHelpers is no longer required. See Selenium's explanation of expected conditions for the .NET approach.

Document readiness only describes the initial document, not the completion of every JavaScript update. Wait for a specific element or application condition after dynamic changes. Avoid mixing implicit and explicit waits, because this can make timeout behavior unpredictable. See Selenium's waiting strategies.

The code đź’Ş

using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;

public static class SeleniumExtensions
{
    private static WebDriverWait CreateWait(IWebDriver driver, int timeout)
    {
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeout));
        wait.IgnoreExceptionTypes(
            typeof(NoSuchElementException),
            typeof(StaleElementReferenceException));
        return wait;
    }

    public static void WaitTillDocumentReady(this IWebDriver driver, int timeout = 180)
    {
        CreateWait(driver, timeout).Until(d =>
            string.Equals(d.Scripts().ExecuteScript("return document.readyState") as string,
                "complete", StringComparison.Ordinal));
    }

    public static IWebElement WaitUntilElementExists(
        this IWebDriver driver, By locator, int timeout = 20)
    {
        return CreateWait(driver, timeout).Until(d => d.FindElement(locator));
    }

    public static IWebElement WaitUntilElementVisible(
        this IWebDriver driver, By locator, int timeout = 20)
    {
        return CreateWait(driver, timeout).Until(d =>
        {
            var element = d.FindElement(locator);
            return element.Displayed ? element : null;
        });
    }

    public static IWebElement WaitUntilElementClickable(
        this IWebDriver driver, By locator, int timeout = 20)
    {
        return CreateWait(driver, timeout).Until(d =>
        {
            var element = d.FindElement(locator);
            return element.Displayed && element.Enabled ? element : null;
        });
    }

    // Use only when the click is expected to replace the old element.
    public static void ClickAndWaitForPageToLoad(
        this IWebDriver driver, By locator, int timeout = 120)
    {
        var element = driver.WaitUntilElementClickable(locator, timeout);
        element.Click();
        CreateWait(driver, timeout).Until(d =>
        {
            try
            {
                _ = element.Enabled;
                return false;
            }
            catch (StaleElementReferenceException)
            {
                return true;
            }
        });
        driver.WaitTillDocumentReady(timeout);
    }

    // Presence means located in the DOM; the element may still be hidden.
    public static bool WaitTillElementPresent(
        this IWebDriver driver, By locator, int timeout = 180)
    {
        try
        {
            driver.WaitUntilElementExists(locator, timeout);
            return true;
        }
        catch (WebDriverTimeoutException)
        {
            return false;
        }
    }

    public static bool IsElementPresent(this IWebDriver driver, By locator)
    {
        try
        {
            driver.FindElement(locator);
            return true;
        }
        catch (NoSuchElementException)
        {
            return false;
        }
    }

    public static IJavaScriptExecutor Scripts(this IWebDriver driver)
    {
        return (IJavaScriptExecutor)driver;
    }
}

What these helpers guarantee

  • Presence and visibility are separate conditions. WaitTillElementPresent returns false on timeout; the element-returning wait methods throw WebDriverTimeoutException.
  • Missing and stale elements are retried during waits. Invalid selectors, lost sessions, and other driver failures propagate instead of becoming a misleading false.
  • “Clickable” means visible and enabled. An overlay can still intercept a click.
  • ClickAndWaitForPageToLoad expects the original element to become stale, then waits for document readiness. For a single-page application that keeps the element, click it and wait for the application's actual result instead. Its waits each have their own timeout.

Verification

On 3 October 2026, this snippet compiled and passed 18 checks using .NET 10 and Selenium 4.49.0 with simulated driver responses. The checks covered presence versus visibility, timeouts, stale-element retries, document readiness, navigation staleness, and propagation of driver failures. No real browser was launched for these checks. Browser behavior depends on the application, browser, and driver; verify those combinations in your own test suite.