← Back to Blog

5 Playwright Tips That Saved My Test Suite

Practical lessons learned from maintaining a large Playwright test suite in production — what works, what doesn't, and what I wish I knew sooner.

After running Playwright tests in production for over a year, I’ve collected a handful of lessons that made a real difference. Here are five that I wish I’d known from the start.

1. Use page.getByRole() Over CSS Selectors

CSS selectors break when developers rename classes. Role-based selectors are tied to semantics, not implementation.

// Fragile
await page.click('.btn-primary-submit');

// Resilient
await page.getByRole('button', { name: 'Submit' }).click();

2. Avoid Hard waitForTimeout Calls

page.waitForTimeout(2000) is a code smell. It makes tests slow and still flaky. Use waitForSelector or waitForResponse instead.

// Don't do this
await page.waitForTimeout(2000);

// Do this
await page.waitForSelector('[data-testid="dashboard"]');

3. Isolate State with beforeEach and Storage State

Don’t rely on test order. Each test should set up its own state. Use Playwright’s storageState to reuse authenticated sessions without logging in every time.

4. Use expect Assertions, Not Manual Checks

Playwright’s expect has built-in auto-retry. If you write if (await page.isVisible(...)) you lose the retry logic and get flaky tests.

5. Tag Your Tests

Use Playwright’s --grep feature with tags to run only smoke tests on every deploy and the full suite on nightly runs.

test('critical login flow @smoke', async ({ page }) => { ... });

These five habits cut our flaky test rate by over 60%. Small changes, big impact.