Cheat sheet2-page PDF

Locators cheat sheet: CSS, XPath, Playwright and Selenium

Find the right element every time. Every CSS selector, XPath and locator below was tried on the sample form shown first.

Preview of the Locators cheat sheet: CSS, XPath, Playwright and Selenium PDF file

Sample page used in every example

<form id="login-form" class="card login">
  <h2>Log in</h2>
  <label for="email">Email</label>
  <input id="email" name="email" type="email"
         placeholder="you@example.com">
  <label for="pass">Password</label>
  <input id="pass" name="password" type="password">
  <input id="remember" type="checkbox" checked>
  <label for="remember">Remember me</label>
  <button type="submit" class="btn btn-primary"
          data-testid="login-btn">Log in</button>
  <a href="/forgot-password">Forgot password?</a>
</form>

Try a locator before you use it

Open DevTools (F12), go to Elements and press Ctrl+F (Cmd+F on Mac). Type a CSS selector or an XPath. DevTools shows how many elements match.

Or type this in the Console tab:

$$("input[type='email']")
$x("//button[text()='Log in']")

Aim for 1 of 1. If it says 1 of 3, your test may click the wrong one.

CSS selectors

LOCATORFINDS
#emailElement with id="email"
.btn-primaryElement with that class
.btn.btn-primaryHas both classes
input[name='password']Attribute equals a value
a[href*='forgot']Attribute contains text
input[id^='em']Starts with …
a[href$='password']Ends with …
[data-testid='login-btn']The Log in button, by test id
#login-form > h2Direct child only
form inputAny input inside the form (3)
label + inputInput right after a label (2)
form > input:nth-of-type(2)Second input: the password
input:checkedTicked checkbox or radio
input:not([type='checkbox'])Every input except the checkbox

XPath

LOCATORFINDS
//input[@id='email']Attribute equals a value
//button[text()='Log in']Exact text
//*[text()='Log in']Finds 2: the heading and the button
//button[normalize-space()='Log in']Text, ignoring extra spaces
//a[contains(text(),'Forgot')]Text contains
//button[contains(@class,'btn-primary')]Class contains
//input[starts-with(@id,'em')]Starts with …
//input[@type='email' and @name='email']Two conditions
(//input)[2]Second input on the page
//label[text()='Email']/following-sibling::input[1]Input that comes after the Email label
//input[@id='pass']/preceding-sibling::label[1]Label just before the password box
//button/parent::formGo up one level (same as /..)
//input[@id='email']/ancestor::formGo up to any level

Playwright: built-in locators

Playwright’s docs suggest these first, in roughly this order. They find elements the way a user sees them.

CODE (JAVASCRIPT)FINDS
page.getByRole('button', { name: 'Log in' })Button by its visible name
page.getByLabel('Email')Input linked to that label
page.getByPlaceholder('you@example.com')Input by placeholder
page.getByRole('link', { name: 'Forgot password?' })Link by its text
page.getByRole('checkbox', { name: 'Remember me' })Checkbox by its label
page.getByText('Forgot password?')Any element by its text
page.getByTestId('login-btn')By data-testid
page.locator('#email')CSS or XPath when nothing else fits

Selenium: the 8 By locators

Use with driver.findElement(...) (Java shown).

CODE (JAVA)FINDS
By.id("email")Fastest and clearest, if the id is stable
By.name("password")The name attribute
By.className("btn-primary")One class name only, no spaces
By.cssSelector("button.btn-primary")Any CSS selector
By.xpath("//a[contains(text(),'Forgot')]")Any XPath
By.linkText("Forgot password?")Link with this exact text
By.partialLinkText("Forgot")Link text contains
By.tagName("form")By tag

A good locator…

  • Uses what the user sees: a role, label or text
  • Or a test id added for testing: data-testid
  • Is short and matches exactly one element
  • Still works after a design change

Avoid…

  • Full paths: /html/body/div[2]/div/form/button
  • Auto-made ids or classes: css-1x9k2, react-select-3
  • Index alone, like (//button)[7]
  • contains(@class,'btn'): also matches btn-primary, btn-link…

CSS or XPath?

CSS is shorter, easier to read and works everywhere. Use it by default.

XPath can match by visible text and can walk up to a parent. CSS can do neither.

No good locator at all? Ask the developers to add a data-testid. It is a small change.

“Element not found”? Check these

Not loaded yet. Playwright waits on its own. In Selenium use WebDriverWait, not Thread.sleep.

Inside an iframe. Switch to it first: driver.switchTo().frame() or page.frameLocator().

Inside a shadow DOM. Playwright CSS goes in on its own; Selenium needs getShadowRoot().

Two matches, one hidden, like a desktop and a mobile menu. Be more specific.

More cheat sheets and checklists

All files