8. Page objects, data, and waits
Test Automation · 48 pages source format
Structure is kindness to future-you. Hide selectors behind intent, seed data via API, and wait for conditions — never sleep.
What you'll learn
- POM / screenplay ideas
- Fixture data
- Deterministic waits
Extract a page object
Methods named like user intent: loginAs(user), addItem(name). Selectors live in one place.
// Conceptual POM
class LoginPage {
constructor(page) { this.page = page; }
async loginAs(user, pass) {
await this.page.getByPlaceholder("Username").fill(user);
await this.page.getByPlaceholder("Password").fill(pass);
await this.page.getByRole("button", { name: "Login" }).click();
}
}Do this now
Refactor your login test into a LoginPage with loginAs(username, password) method.
Data without shared pollution
Unique emails, API create/delete, or transactional resets. Shared “admin” users create ghosts.
Do this now
Generate a unique user per run (timestamp or uuid). Document data strategy in DATA.md.
Pro tip. Prefer API setup + UI assert for speed.
Ban hard sleeps
Wait for network idle, element visible, or response. Sleeps hide races until CI load exposes them.
Do this now
Find any waitForTimeout/sleep/Thread.sleep. Replace with a condition wait. Zero tolerance policy.
Test isolation
Each test should setup and teardown its own state. Order-dependent suites are debt.
Do this now
Run your tests in random order. Fix any that fail when shuffled.