Finding elements
How to point at a button, and where the right answer comes from.
Two questions that get muddled together:
- What kind of locator should I use? Answered here.
- What is this particular button called in my app? Answered by your app’s notes.
Prefer what a person would say
In order of preference:
// Best — matches what a user sees and a screen reader announces
page.getByRole('button', { name: 'Log in' });
// Good — for form fields
page.getByLabel('Email');
page.getByPlaceholder('you@example.com');
// Fine — when there is nothing else to grab hold of
page.getByTestId('checkout-submit');
// Avoid
page.locator('.btn-primary > span:nth-child(2)');
Why not CSS
page.locator('.btn-primary');
That is tied to how the page is styled. Somebody renames a class during a redesign, your test breaks, and nothing about the application actually changed.
getByRole('button', { name: 'Log in' }) is tied to what the button is. It
only breaks when the button genuinely changes — which is exactly when you want to
hear about it.
Never invent one
If you do not know what something is called, look it up.
npx @wiluszdamian/cue locator "checkout button"
If Cue does not know either, it says so and tells you which page to go and look at. It will not make one up, and neither should you.
An invented locator does not fail with “I guessed”. It fails as though your app is broken — and that is an afternoon gone.
Organising them
Locators live in a page object, in three groups.
export class LoginPage {
// --- inputs ---
get emailField() {
return this.page.getByLabel('Email');
}
// --- actions ---
get logInButton() {
return this.page.getByRole('button', { name: 'Log in' });
}
// --- feedback ---
get errorMessage() {
return this.page.getByRole('alert');
}
}
The grouping earns its keep because the feedback ones are what your checks use, and they are the ones you go hunting for. Buried among the click targets, they make the file harder to read every single time.