Write your first test
The shape of a test here, and the two files it lives in.
Cue set up a working example when you ran init. Let’s walk through it,
because everything else follows the same shape.
Two files, two jobs
A test here is split across two files, and the split is the whole idea.
The test says what a person does. Read it out loud and it should sound like a sentence.
test('a known user reaches their dashboard', { tag: ['@smoke'] }, async ({ loginPage }) => {
await loginPage.goto();
await loginPage.logIn('user@example.com', 'correct-password');
await expect(loginPage.welcomeHeading).toBeVisible();
});
The page object says where things are. All the fiddly details live here.
export class LoginPage {
get emailField() {
return this.page.getByLabel('Email');
}
get logInButton() {
return this.page.getByRole('button', { name: 'Log in' });
}
get welcomeHeading() {
return this.page.getByRole('heading', { name: /welcome/i });
}
}
Why bother splitting them
When a designer renames a button, you change one line in one file. Without the split, you’d search the whole test suite for every place that button appears.
That’s it. That’s the entire reason.
Run it
npx playwright test
Or just the fast ones:
npx playwright test --grep @smoke
Three habits that matter
Wait for things, not for seconds
// Don't
await page.waitForTimeout(5000);
// Do — this waits exactly as long as it needs to
await expect(loginPage.welcomeHeading).toBeVisible();
Tag every test
test('user can log in', { tag: ['@smoke'] }, async ({ loginPage }) => {
Tags decide when a test runs. @smoke means “fast and reliable enough to run on
every push”. Without a tag, a test either runs everywhere or nowhere — and both
are wrong.
Let the fixture hand you the page
// Do — it arrives ready to use
test('...', async ({ loginPage }) => {
// Don't — now this test knows how to build one
const loginPage = new LoginPage(page);
If a check stops you
You’ll see a message explaining what’s wrong, why, and what to write instead. Want the longer version?
npx @wiluszdamian/cue explain no-hard-waits
Next
- Teach it about your app — So nobody has to guess what a button is called.
- Finding elements — How to choose a good way to point at something.