Playwright
Tips & Best Practices
Improve your Playwright tests with these tips and best practices.
Writing Reliable Tests
Use Proper Selectors
Prefer stable selectors that won't break with UI changes:
// ✅ Good - uses data-testid
await page.getByTestId('submit-button').click();
// ✅ Good - uses role
await page.getByRole('button', { name: 'Submit' }).click();
// ❌ Avoid - fragile CSS selectors
await page.click('.btn-primary:nth-child(2)');Wait for Elements
Always wait for elements before interacting:
// ✅ Good - waits for element
await page.getByRole('button').waitFor();
await page.getByRole('button').click();
// ✅ Better - auto-waiting with expect
await expect(page.getByRole('button')).toBeVisible();Performance Tips
Reuse Authentication State
Save login state to avoid logging in for every test:
// Save auth state after login
await page.context().storageState({ path: 'auth.json' });
// Reuse in other tests
const context = await browser.newContext({ storageState: 'auth.json' });Parallel Testing
Structure tests to run in parallel when possible:
test.describe.configure({ mode: 'parallel' });
test('test 1', async ({ page }) => { /* ... */ });
test('test 2', async ({ page }) => { /* ... */ });Debugging
Use Trace Viewer
SupaGuard captures traces for failed tests. View them and:
- See screenshots at each step
- Inspect network requests
- Debug timing issues
Add Meaningful Assertions
// ✅ Good - clear failure message
await expect(page.getByRole('heading')).toHaveText('Welcome back, John');
// ❌ Less helpful
await expect(page.locator('h1')).toBeTruthy();