Finding elements on a page is the single most important skill in UI automation. Get it wrong and your tests break every time a developer changes a CSS class. Get it right and your tests survive redesigns, refactors, and even framework migrations. Playwright has a strong opinion about how you should find elements. Let me explain that opinion.
In Selenium and Cypress, people write selectors like #login-btn, .card-title, or div > form > input:nth-child(2). These are CSS selectors. They work, but they are tied to implementation details. A developer renames a class? Test breaks. They restructure the HTML? Test breaks. They switch from div to section? Test breaks. None of these changes affect what the user sees. The button still says "Login." The heading still says "Product Details." The input still has a label "Email."
Playwright says: find elements the way a user would find them. A user does not think "I will click the element with class btn-primary." They think "I will click the Login button." That is exactly what getByRole('button', { name: 'Login' }) does. It finds the element by its role (button) and its visible name (Login). No CSS. No XPath. No fragile selectors.
A locator is a recipe for finding an element. Not the element itself -- the recipe. This is a critical distinction. When you write page.getByRole('button', { name: 'Submit' }), Playwright does not immediately search the DOM. It stores the instruction. The actual search happens when you perform an action like .click() or an assertion like toBeVisible(). This means the locator always finds the fresh, current element -- not a stale reference from 3 seconds ago.
In Selenium, you grab an element reference with findElement() and it can go stale if the page re-renders. In Playwright, locators never go stale because they re-query the DOM every time you use them. No more StaleElementReferenceException.
When you call locator.click(), Playwright does not just blindly click. It waits for the element to be visible, enabled, stable (not moving), and not obscured by another element. If the element appears after 2 seconds because of an API call, Playwright waits. No explicit Thread.sleep() or waitForElement() calls needed. This alone eliminates 80% of flaky tests.
Key Point: Playwright auto-checks all five conditions before performing any action. This is called actionability. You do not write wait logic. Playwright handles it.
Q: Why does Playwright recommend user-facing locators over CSS selectors?
A: CSS selectors are tied to implementation details -- class names, IDs, HTML structure. These change frequently during development. User-facing locators like getByRole and getByText target what users actually see -- button labels, heading text, form labels. These rarely change even during major redesigns. It also encourages developers to write accessible HTML, which benefits both testing and real users with screen readers.
Key Point: Playwright locators find elements the way users do -- by role, text, and label. They auto-wait, auto-retry, and never go stale.