Selenium cheat sheet

A scannable Selenium reference: 21 short snippets across 10 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
WebDriver setupSelenium is a client library that speaks the W3C WebDriver protocol over HTTP. A driver binary - chromedriverlesson
Locators and waitsPrefer a hook the application controls, such as an id or a data-testid attribute, over anything derived from layout orlesson
Browser options and headless executionAlmost every per-run setting - headless, window size, download path, proxy, extra capabilities - goes through anlesson
Common element interactionsA custom dropdown built from divs is not a <select> and Select will raise UnexpectedTagNameException. Click thelesson
Alerts, frames and windowsThe stale element error that follows a frame or window switch is not a wait problem. The reference belongs to alesson
Advanced user actions with ActionChainsActionChains builds a sequence of low-level input actions and executes them together. Nothing happens until perform()lesson
JavaScript execution and CDPCDP commands are Chrome-specific. Firefox has no equivalent through Selenium, so anything built on them belongs in anlesson
Screenshots, logs and debugging flaky testsAsserting on console errors is one of the highest-value checks you can add: it catches failed asset loads, unhandledlesson
Structuring a suite: page objects, pytest and parallelismOrganise a growing Selenium suite with page objects, fixtures and markers, then run it in parallel without the testslesson
Selenium Grid, Docker and CIGrid separates the session request from the browser that fulfils it. Your test connects to the Grid's router; the Gridlesson

Quick snippets

WebDriver setup

The three moving parts

# pip install selenium
from selenium import webdriver

driver = webdriver.Chrome()        # Selenium Manager resolves the driver binary
driver.get("https://example.com")
print(driver.title)
driver.quit()

Full lesson: WebDriver setup →

Locators and waits

Finding elements

from selenium.webdriver.common.by import By

# stable: a hook the application owns
driver.find_element(By.CSS_SELECTOR, "[data-testid='checkout']").click()

# brittle: derived from layout and position
driver.find_element(By.XPATH, "/html/body/div[3]/div[2]/button[1]").click()

Implicit versus explicit waits

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)

# wait for one condition on one element, then continue
element = wait.until(EC.element_to_be_clickable((By.ID, "confirm")))
element.click()

wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".toast")))
wait.until(EC.number_of_windows_to_be(2))

Full lesson: Locators and waits →

Browser options and headless execution

The options object is where configuration lives

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

options = Options()
options.add_argument("--window-size=1440,900")
options.add_argument("--disable-dev-shm-usage")   # small /dev/shm in containers
options.add_argument("--no-sandbox")              # containers only, see the warning
options.add_argument("--lang=en-GB")

driver = webdriver.Chrome(options=options)
print(driver.capabilities["browserVersion"])
driver.quit()

Headless mode and sizing

# force the same configuration locally as in CI
CI=1 python -m pytest tests/test_smoke.py -q

# check the real viewport your test sees
python -c "from selenium import webdriver; d=webdriver.Chrome(); print(d.execute_script('return [innerWidth, innerHeight]')); d.quit()"

Downloads, profiles and Firefox

from selenium.webdriver.firefox.options import Options as FirefoxOptions

ff = FirefoxOptions()
ff.add_argument("-headless")
ff.set_preference("browser.download.folderList", 2)
ff.set_preference("browser.download.dir", download_dir)
ff.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/csv")
driver = webdriver.Firefox(options=ff)

Full lesson: Browser options and headless execution →

Common element interactions

Dropdowns, checkboxes and radios

terms = driver.find_element(By.ID, "terms")
if not terms.is_selected():
    terms.click()
assert terms.is_selected()

plan = driver.find_element(By.CSS_SELECTOR, "input[name=plan][value=annual]")
plan.click()
assert plan.get_attribute("checked") is not None

Reading values, uploads and state

# remote grids: ship the file with the session
from selenium.webdriver.remote.file_detector import LocalFileDetector

driver.file_detector = LocalFileDetector()
driver.find_element(By.CSS_SELECTOR, "input[type=file]").send_keys("invoice.pdf")

Full lesson: Common element interactions →

Alerts, frames and windows

Windows and tabs

# a helper worth keeping
def switch_to_new_window(driver, before, timeout=10):
    WebDriverWait(driver, timeout).until(
        lambda d: len(d.window_handles) > len(before)
    )
    new = [h for h in driver.window_handles if h not in before][0]
    driver.switch_to.window(new)
    return new

Full lesson: Alerts, frames and windows →

Advanced user actions with ActionChains

Scrolling within a chain

# the action many people want: scroll, then interact, atomically
(
    ActionChains(driver)
    .scroll_to_element(checkout_button)
    .move_to_element(checkout_button)
    .click()
    .perform()
)

Full lesson: Advanced user actions with ActionChains →

JavaScript execution and CDP

execute_script

# async: the script must call the callback exactly once
delay = driver.execute_async_script("""
    const done = arguments[arguments.length - 1];
    setTimeout(() => done(performance.now()), 500);
""")
print("resolved after", round(delay))

Shadow DOM

# WebDriver's own locators do not cross a shadow boundary
host = driver.find_element(By.CSS_SELECTOR, "checkout-widget")

inner = driver.execute_script(
    "return arguments[0].shadowRoot.querySelector('button.primary');",
    host,
)

# Selenium 4 supports shadow roots directly on the element
shadow = host.shadow_root
button = shadow.find_element(By.CSS_SELECTOR, "button.primary")
button.click()

Chrome DevTools Protocol

# send custom headers on every request
driver.execute_cdp_cmd("Network.enable", {})
driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {
    "headers": {"X-Test-Run": "ci-1234"}
})

# block third-party analytics so it cannot slow the suite down
driver.execute_cdp_cmd("Network.setBlockedURLs", {
    "urls": ["*google-analytics.com*", "*segment.io*"]
})

Full lesson: JavaScript execution and CDP →

Screenshots, logs and debugging flaky tests

Logs that mean something

from selenium.webdriver.chrome.options import Options

options = Options()
options.set_capability("goog:loggingPrefs", {
    "browser": "ALL",        # console output and page errors
    "performance": "ALL",
})

# after the test, fail on unexpected console errors
errors = [e for e in driver.get_log("browser") if e["level"] == "SEVERE"]
assert not errors, errors

Diagnosing intermittent failures

# loop until it fails, then keep the artefacts
for i in $(seq 1 20); do
  python -m pytest tests/test_checkout.py -q || break
done

# retries as a diagnostic, not a fix
python -m pytest --reruns 3 --reruns-delay 1 -q

Diagnosing intermittent failures

# a wait that means what the test needs
from selenium.webdriver.support import expected_conditions as EC

# wrong: the element exists but is not yet interactive
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "pay"))
)

# right: it is there, visible and enabled
WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "pay"))
)

Full lesson: Screenshots, logs and debugging flaky tests →

Structuring a suite: page objects, pytest and parallelism

Page objects and components

# tests/test_login.py
import pytest
from pages.login import LoginPage

@pytest.mark.smoke
def test_rejects_a_wrong_password(driver, base_url):
    page = LoginPage(driver, base_url).open()
    page.submit("[email protected]", "wrong")
    assert "Incorrect email or password" in page.error()

Markers and parallel execution

# pytest.ini
[pytest]
markers =
    smoke: fast checks on the critical path
    slow: long-running integration flows
    auth: exercises the real login mechanism
addopts = -ra --strict-markers

Markers and parallel execution

pytest -m smoke -q                     # the fast subset
pytest -m "not slow" -q                 # everything except the slow flows
pytest -m "not auth" -q                 # runs that reuse a saved session

pytest -n 4 --dist loadfile -q          # four workers, one file per worker
pytest --reruns 2 --reruns-delay 1 -m smoke -q

Full lesson: Structuring a suite: page objects, pytest and parallelism →

Selenium Grid, Docker and CI

Docker Compose

docker compose up -d --scale chrome=3
open http://localhost:4444        # the Grid console
docker compose logs -f chrome
docker compose down -v

A CI job with reports

# fail the build from the report, with a readable summary
python -m junitparser merge reports/*.xml merged.xml
python -m junit2html merged.xml reports/summary.html

Full lesson: Selenium Grid, Docker and CI →

FAQ

Is this Selenium cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 10 lessons of the Selenium course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full Selenium course — it carries the worked explanations, the edge cases and the exercises behind every line here.

AI Basics AI Agents Math for AI Machine Learning scikit-learn TensorFlow

Last refreshed 2026-09-27.