supaguardsupaguardDocs
Playwright

Playwright Tips & Best Practices for Reliable Tests

Write more reliable Playwright tests with proper selectors, auto-waiting, authentication state reuse, parallel testing, and debugging techniques.

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();

On this page