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.