Selenium Rules for AI Assistants

These rules help AI coding assistants generate correct, idiomatic Selenium code. They cover the modern Selenium 4 API across all official language bindings.


Core Principles

  • Never use time.sleep() / Thread.sleep() / sleep(). Use explicit waits.
  • Never hardcode driver paths or versions. Selenium Manager resolves them automatically.
  • Always call driver.quit() in a finally block or equivalent to release browser resources.
  • Prefer CSS selectors over XPath for readability and speed. Prefer id and name first.
  • Use Page Object Model (POM) to separate page structure from test logic.

Installation

LanguageCommand / Config
Pythonpip install selenium
JavaMaven: selenium-java artifact, group org.seleniumhq.selenium
JavaScriptnpm install selenium-webdriver
Rubygem install selenium-webdriver
.NETdotnet add package Selenium.WebDriver

Selenium Manager (bundled since Selenium 4.6) automatically downloads the correct browser driver. No webdriver-manager, chromedriver-binary, or manual driver setup is needed.


Starting a Browser

Python

from selenium import webdriver

driver = webdriver.Chrome()   # Selenium Manager handles chromedriver automatically
driver.get("https://example.com")

Java

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

WebDriver driver = new ChromeDriver();  // Selenium Manager handles chromedriver
driver.get("https://example.com");

JavaScript (Node.js)

const { Builder } = require('selenium-webdriver');

const driver = await new Builder().forBrowser('chrome').build();
await driver.get('https://example.com');

Ruby

require 'selenium-webdriver'

driver = Selenium::WebDriver.for :chrome
driver.navigate.to 'https://example.com'

.NET (C#)

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

IWebDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://example.com");

Always Quit the Driver

Python

try:
    driver.get("https://example.com")
finally:
    driver.quit()

Java

try {
    driver.get("https://example.com");
} finally {
    driver.quit();
}

JavaScript

try {
    await driver.get('https://example.com');
} finally {
    await driver.quit();
}

Ruby

begin
    driver.navigate.to 'https://example.com'
ensure
    driver.quit
end

.NET

using (IWebDriver driver = new ChromeDriver()) {
    driver.Navigate().GoToUrl("https://example.com");
}

Locating Elements

Priority order: id > name > css > link_text > xpath

Python

from selenium.webdriver.common.by import By

driver.find_element(By.ID, "username")
driver.find_element(By.CSS_SELECTOR, "input[name='q']")
driver.find_elements(By.CSS_SELECTOR, "ul > li")

Java

import org.openqa.selenium.By;

driver.findElement(By.id("username"));
driver.findElement(By.cssSelector("input[name='q']"));
driver.findElements(By.cssSelector("ul > li"));

JavaScript

const { By } = require('selenium-webdriver');

await driver.findElement(By.id('username'));
await driver.findElement(By.css("input[name='q']"));
await driver.findElements(By.css('ul > li'));

Ruby

driver.find_element(id: 'username')
driver.find_element(css: "input[name='q']")
driver.find_elements(css: 'ul > li')

.NET

driver.FindElement(By.Id("username"));
driver.FindElement(By.CssSelector("input[name='q']"));
driver.FindElements(By.CssSelector("ul > li"));

Explicit Waits (REQUIRED — never use sleep)

Wait for a specific condition before interacting with an element.

Python

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

wait = WebDriverWait(driver, timeout=10)
element = wait.until(EC.element_to_be_clickable((By.ID, "submit")))
element.click()

Java

import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit")));
element.click();

JavaScript

const { until } = require('selenium-webdriver');

const element = await driver.wait(until.elementIsVisible(
    driver.findElement(By.id('submit'))
), 10000);
await element.click();

Ruby

wait = Selenium::WebDriver::Wait.new(timeout: 10)
element = wait.until { driver.find_element(id: 'submit') }
element.click

.NET

using OpenQA.Selenium.Support.UI;
using SeleniumExtras.WaitHelpers;

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
var element = wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("submit")));
element.Click();

Common Expected Conditions

ConditionPythonJava
Element visiblevisibility_of_element_locatedExpectedConditions.visibilityOf
Element clickableelement_to_be_clickableExpectedConditions.elementToBeClickable
Text present in elementtext_to_be_present_in_elementExpectedConditions.textToBePresentInElement
Alert presentalert_is_presentExpectedConditions.alertIsPresent
URL containsurl_containsExpectedConditions.urlContains
Title containstitle_containsExpectedConditions.titleContains
Element stalenessstaleness_ofExpectedConditions.stalenessOf

Browser Options & Capabilities

Run headless (Python example)

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)

Other common Chrome options

options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-gpu")

Set implicit wait (use sparingly — prefer explicit waits)

driver.implicitly_wait(5)  # seconds

Do not mix implicit and explicit waits — this causes unpredictable timeouts.


Page Object Model

Encapsulate page structure in dedicated classes to improve maintainability.

Python example

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


class LoginPage:
    URL = "https://example.com/login"

    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def open(self):
        self.driver.get(self.URL)
        return self

    def login(self, username, password):
        self.wait.until(EC.element_to_be_clickable((By.ID, "username"))).send_keys(username)
        self.driver.find_element(By.ID, "password").send_keys(password)
        self.driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()
        return self

Actions (Mouse & Keyboard)

Python

from selenium.webdriver.common.action_chains import ActionChains

ActionChains(driver).move_to_element(element).click().perform()
ActionChains(driver).double_click(element).perform()
ActionChains(driver).context_click(element).perform()
ActionChains(driver).drag_and_drop(source, target).perform()

Sending keyboard input

from selenium.webdriver.common.keys import Keys

element.send_keys("Hello World")
element.send_keys(Keys.RETURN)
element.clear()

Handling Alerts, Frames, and Windows

Alerts (Python)

alert = driver.switch_to.alert
alert.accept()   # OK
alert.dismiss()  # Cancel
alert.send_keys("text")

Frames

driver.switch_to.frame("frame-id")
driver.switch_to.default_content()

Multiple Windows/Tabs

main_window = driver.current_window_handle
driver.switch_to.new_window('tab')
driver.switch_to.window(main_window)
driver.close()
driver.switch_to.window(main_window)

Taking Screenshots

driver.save_screenshot("screenshot.png")
element.screenshot("element.png")

JavaScript Execution

Use sparingly — prefer native Selenium interactions when possible.

result = driver.execute_script("return document.title;")
driver.execute_script("arguments[0].click();", element)
driver.execute_script("arguments[0].scrollIntoView(true);", element)

Working with Selects and Dropdowns

Python

from selenium.webdriver.support.select import Select

select = Select(driver.find_element(By.ID, "dropdown"))
select.select_by_visible_text("Option 1")
select.select_by_value("opt1")
select.select_by_index(0)
options = select.options

Selenium Grid

Run tests in parallel across different browsers and machines.

from selenium import webdriver

driver = webdriver.Remote(
    command_executor="http://localhost:4444",
    options=webdriver.ChromeOptions()
)

Start a local Grid (requires Java):

selenium-manager --grid
# Or download and run:
java -jar selenium-server.jar standalone

BiDi (WebDriver BiDirectional Protocol)

Selenium 4 supports real-time browser events via BiDi.

Python: Listen for console logs

from selenium import webdriver
from selenium.webdriver.common.bidi.console import Console

driver = webdriver.Chrome()
with driver.bidi_connection() as conn:
    session = conn.session
    # Use BiDi APIs for log listening, network interception, etc.

Selenium Manager CLI

Selenium Manager is also a standalone CLI tool for driver and browser management.

# Resolve/download chromedriver for the installed Chrome version
selenium-manager --browser chrome

# Download a specific Firefox version
selenium-manager --browser firefox --browser-version 120

# Generate a Selenium skills reference file
selenium-manager --init-skills

# Generate these LLM rules in rules/selenium.md
selenium-manager --init-rules

Common Errors & Fixes

ErrorCauseFix
NoSuchElementExceptionElement not in DOM yetUse explicit wait
StaleElementReferenceExceptionDOM updated after element was foundRe-locate the element
ElementNotInteractableExceptionElement hidden or coveredScroll into view or wait for visibility
TimeoutExceptionCondition never met in waitIncrease timeout or check selector
WebDriverException: chrome not reachableBrowser crashed or closedCheck browser startup options
SessionNotCreatedExceptionDriver/browser version mismatchLet Selenium Manager resolve the driver

Anti-Patterns to Avoid

  • time.sleep(5) — use WebDriverWait instead
  • driver.find_element(By.XPATH, "/html/body/div[3]/span[2]") — fragile absolute XPath
  • ❌ Hardcoding driver paths like ChromeDriver("/usr/bin/chromedriver") — let Selenium Manager handle it
  • ❌ Mixing implicit and explicit waits
  • ❌ Not calling driver.quit() — causes zombie browser processes
  • driver.get() inside a loop without proper cleanup
  • ❌ Using execute_script to click elements that are interactable natively

Resources