Concept
Multi-Browser Architecture
Unlike Cypress which runs inside the browser context, Playwright controls browsers out-of-process using native browser debugging protocols (like Chrome DevTools Protocol). This enables:
- True Multi-Browser Support: Tests run concurrently across Chromium, WebKit (Safari's engine), and Firefox.
- Isolation via Contexts: Browser Contexts act like incognito profiles. You can open multiple isolated contexts in a single browser instance, letting you test multi-user chat applications in a single run.
import { test, expect } from '@playwright/test';
test('basic login test', async ({ page }) => {
await page.goto('/login');
// Use Locators (automatic waiting, lazy evaluation)
await page.getByLabel('Email').fill('ada@example.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Log in' }).click();
// Assert URL changes
await expect(page).toHaveURL(/.*dashboard/);
});Locators vs Selectors
Playwright introduces Locators. Locators represent a view selector that resolves elements dynamically on interaction (lazy resolution). They carry built-in auto-waiting parameters, checking that elements are:
- Visible
- Enabled
- Stable (not animating)
- Editable
Common Mistakes
1. Using CSS selector chains instead of semantic Locators
Querying elements via .btn-primary or #input-field-2 makes tests break when layouts shift. Always use semantic locator strategies (like page.getByRole or page.getByLabel) to ensure tests are resilient and verify accessibility.
2. Not awaiting Playwright calls
All Playwright actions (visits, clicks, inputs, assertions) return Promises. Forgetting to prepend await before a statement causes execution to bypass the step, leading to race conditions and test failures:
// ❌ WRONG: Promise is not awaited
page.click('button');
// CORRECT: Explicitly await resolution
await page.click('button');Best Practices
- Leverage Test Parallelism: Playwright runs tests in parallel across separate worker processes by default. Ensure your database seed scripts do not conflict when tests execute concurrently.
- Trace Viewer: Enable traces (
trace: 'on-first-retry') in configs to capture screenshots, console logs, and network histories for debugging pipeline failures. - Use Web First Assertions: Always assert states using
expect(locator).toBeVisible()rather than checking DOM boolean values, enabling automatic waiting during checks.
