Overview
Playwright is a powerful end-to-end testing framework that allows you to test your application in real browsers. Sabo’s testing setup provides:- Cross-browser testing (Chromium, Firefox, WebKit)
- Mobile viewport testing (Pixel 5, iPhone 12)
- Comprehensive coverage across 16 spec files spanning authentication, dashboard, marketing, blog, changelog, and legal flows
- Authentication helpers for testing protected routes
- Parallel test execution for fast feedback
- Visual debugging with Playwright UI mode
- HTML test reports with screenshots and traces
pnpm build && pnpm start) to ensure tests match real-world behavior.Quick Start
Install Playwright
package.json. If you need to reinstall or update:Run your first test
- Build your application (
pnpm build) - Start a production server (
pnpm starton port 3000) - Run all tests across all configured browsers
- Generate an HTML report
View test results
- Pass/fail status for each test
- Screenshots of failures
- Execution traces for debugging
- Performance metrics
playwright-report/ directory.Test Commands
Sabo provides several npm scripts for different testing workflows:Command Breakdown
pnpm test:e2e - Standard test run
pnpm test:e2e - Standard test run
- Runs all tests in headless mode (no browser UI)
- Tests run in parallel for speed
- Automatically builds and starts your app
- Generates HTML report at the end
- Exit code 0 (success) or 1 (failure) for CI integration
pnpm test:e2e:ui - Interactive UI mode
pnpm test:e2e:ui - Interactive UI mode
- Opens Playwright’s interactive test runner UI
- Watch mode: tests re-run when files change
- Time-travel debugging with DOM snapshots
- Click through test steps one at a time
- Filter tests by name, file, or status
- See test execution in real-time
- Inspect DOM at each step
- View network requests and console logs
- Record new tests by clicking in your app
- Compare screenshots side-by-side
pnpm test:e2e:headed - Visual browser mode
pnpm test:e2e:headed - Visual browser mode
- Tests run with visible browser windows
- See exactly what Playwright sees
- Slower than headless mode
- Useful for debugging timing issues
- Run
pnpm test:e2e:headed - Watch browser open and navigate your app
- Identify visual bugs or timing issues
- Fix and re-run specific tests
pnpm test:e2e:debug - Step-by-step debugging
pnpm test:e2e:debug - Step-by-step debugging
- Opens Playwright Inspector
- Pauses at each test step
- Step forward/backward through test
- Explore locators and selectors
- Modify test code on the fly
- Pick locator: Click any element to generate a selector
- Step over/into: Control test execution
- Console: Run Playwright commands interactively
- Source: View test code with current line highlighted
pnpm test:e2e:report - View test results
pnpm test:e2e:report - View test results
- Opens HTML report in your default browser
- Shows pass/fail status for all tests
- Includes screenshots of failures
- Execution traces for debugging
- Test duration and retry information
- Filter by status (passed/failed/flaky/skipped)
- Search tests by name
- View screenshots and videos
- Download trace files for offline debugging
- Compare test results across runs
Test Configuration
Playwright configuration is defined inplaywright.config.ts at the project root:
Configuration Options Explained
testDir - Test files location
testDir - Test files location
tests/ directory. Playwright looks for files ending in .spec.ts or .test.ts.Sabo’s test structure:fullyParallel - Run tests simultaneously
fullyParallel - Run tests simultaneously
- Without parallel: 85 tests in ~8 minutes
- With parallel (5 workers): 85 tests in ~1.5 minutes
retries - Handle flaky tests
retries - Handle flaky tests
- Network latency
- Resource constraints
- Timing issues in CI environments
workers - Control parallelism
workers - Control parallelism
- Local: Uses all CPU cores (
undefined= auto) - CI: Sequential execution (
1worker) for stability
webServer - Auto-start application
webServer - Auto-start application
- Builds your app (
pnpm build) - Starts production server (
pnpm start) - Waits for server to be ready (checks
url) - Runs tests
- Shuts down server after tests complete
reuseExistingServer:- Local: Reuses running server (faster during development)
- CI: Always starts fresh server (ensures clean state)
Test Structure
Sabo’s tests are organized by feature area intests/e2e/:
Test File Anatomy
Here’s a typical test file structure:Test Patterns
test.beforeEach() - Setup before each test
test.beforeEach() - Setup before each test
- Navigate to a common page
- Set up authentication
- Clear browser state
- Inject test data
Locators - Find elements on the page
Locators - Find elements on the page
- Prefer
getByRolefor accessibility - Use
data-testidfor dynamic content - Avoid brittle selectors (CSS classes that may change)
- Use regex for flexible text matching
Assertions - Verify expected behavior
Assertions - Verify expected behavior
Writing Your First Test
Let’s write a test for a new feature page step by step.Create test file
Import Playwright
Write your first test
Run your test
Add more test cases
Testing Protected Routes
Many pages in your app require authentication. Sabo provides an auth helper to set up authenticated sessions in tests.Authentication Helper
The auth helper is located attests/e2e/helpers/auth.ts:
Using the Auth Helper
test.skip until setupAuthenticatedUser() is configured. Once your test credentials are in place, remove the skips to exercise the protected flows.Setting Up Test Credentials
Create a test user in your Supabase dashboard, then add credentials to.env.test:
.env.test. Either export these variables in your shell before running tests or run commands through the dotenv CLI: pnpm exec dotenv -e .env.test -- pnpm test:e2e. This ensures helpers like setupAuthenticatedUser() can read the credentials.Common Test Patterns
Testing Forms
Testing Navigation
Testing Responsive Design
Testing Conditional Rendering
Testing Async Operations
Debugging Tests
Using Playwright Inspector
Using Playwright Inspector
- Step through: Execute one action at a time
- Pick locator: Click elements to generate selectors
- Console: Run Playwright commands interactively
- Screenshots: Capture state at each step
Using console.log() in tests
Using console.log() in tests
Taking screenshots
Taking screenshots
Using test.skip() and test.only()
Using test.skip() and test.only()
Analyzing trace files
Analyzing trace files
- Timeline of all actions
- Screenshots at each step
- Network requests and responses
- Console logs and errors
- DOM snapshots (time-travel debugging)
Best Practices
1. Write independent tests
1. Write independent tests
2. Use descriptive test names
2. Use descriptive test names
3. Prefer user-facing selectors
3. Prefer user-facing selectors
-
Role-based (best for accessibility)
-
Label-based (for form inputs)
-
Test IDs (for dynamic content)
-
Text content (for static text)
- CSS classes that may change:
.btn-primary-v2-new - Complex CSS selectors:
div > ul > li:nth-child(3) - XPath selectors (hard to maintain)
4. Keep tests fast
4. Keep tests fast
- Use
baseURLto avoid full URLs - Reuse authenticated sessions when possible
- Mock external API calls (if needed)
- Run tests in parallel (
fullyParallel: true) - Use
page.waitForLoadState("domcontentloaded")instead of arbitrary timeouts
6. Clean up test data
6. Clean up test data
CI/CD Integration
Playwright tests can run automatically in your CI/CD pipeline.GitHub Actions Example
Vercel Integration
Run tests before deployment:Troubleshooting
Tests fail with 'Target closed' error
Tests fail with 'Target closed' error
- Check for memory issues (tests using too much RAM)
- Reduce parallel workers:
pnpm exec playwright test --workers=1 - Update Playwright:
pnpm update @playwright/test - Check for infinite loops or long-running operations
Tests are flaky (pass/fail randomly)
Tests are flaky (pass/fail randomly)
- Avoid
page.waitForTimeout()- use specific waits instead - Use auto-waiting assertions (
await expect(...)) - Wait for network to be idle:
await page.waitForLoadState("networkidle") - Increase timeout for slow operations:
{ timeout: 60000 } - Mock external API calls to remove network dependency
'locator.click(): Target element is not visible' error
'locator.click(): Target element is not visible' error
-
Wait for element to be visible first:
-
Scroll element into view:
- Check for overlays or modals covering the element
-
Use
force: true(last resort):
Authentication helper not working
Authentication helper not working
- Verify
.env.testexists with correct credentials - Check test user exists in Supabase dashboard
- Ensure
NEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_ANON_KEYare set - Verify cookie domain matches (
localhostfor local tests) - Check Supabase session is not expired
Tests pass locally but fail on CI
Tests pass locally but fail on CI
- Run tests sequentially on CI:
workers: 1in config - Enable retries on CI:
retries: 2in config - Check environment variables are set in CI
- Increase timeouts for slower CI environments
- Use
webServer.reuseExistingServer: !process.env.CIto ensure fresh server
Playwright browsers not installing
Playwright browsers not installing