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
| Topic | What it covers | |
|---|---|---|
| WebDriver setup | Selenium is a client library that speaks the W3C WebDriver protocol over HTTP. A driver binary - chromedriver | lesson |
| Locators and waits | Prefer a hook the application controls, such as an id or a data-testid attribute, over anything derived from layout or | lesson |
| Browser options and headless execution | Almost every per-run setting - headless, window size, download path, proxy, extra capabilities - goes through an | lesson |
| Common element interactions | A custom dropdown built from divs is not a <select> and Select will raise UnexpectedTagNameException. Click the | lesson |
| Alerts, frames and windows | The stale element error that follows a frame or window switch is not a wait problem. The reference belongs to a | lesson |
| Advanced user actions with ActionChains | ActionChains builds a sequence of low-level input actions and executes them together. Nothing happens until perform() | lesson |
| JavaScript execution and CDP | CDP commands are Chrome-specific. Firefox has no equivalent through Selenium, so anything built on them belongs in an | lesson |
| Screenshots, logs and debugging flaky tests | Asserting on console errors is one of the highest-value checks you can add: it catches failed asset loads, unhandled | lesson |
| Structuring a suite: page objects, pytest and parallelism | Organise a growing Selenium suite with page objects, fixtures and markers, then run it in parallel without the tests | lesson |
| Selenium Grid, Docker and CI | Grid separates the session request from the browser that fulfils it. Your test connects to the Grid's router; the Grid | lesson |
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 newFull 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 -qFull 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.htmlFull lesson: Selenium Grid, Docker and CI →
FAQ
Is this Selenium cheat sheet free to use?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
AI Basics AI Agents Math for AI Machine Learning scikit-learn TensorFlow
Last refreshed 2026-09-27.