This guide covers end-to-end (E2E) testing for the Transcript Create application using Playwright.
Our E2E test suite validates complete user workflows across the entire stack (frontend, API, database). Tests run against real browsers and simulate actual user interactions.
- Node.js 20 or later
- Python 3.11 or later
- PostgreSQL 16 or later
- Docker (optional, for running services)
- Install E2E dependencies:
cd e2e
npm install
npx playwright install --with-deps- Set up environment variables:
# Database connection for seeding test data
export DATABASE_URL="postgresql://postgres:postgres@localhost:5434/transcripts"
# Base URL for the frontend (optional, defaults to http://localhost:5173)
export PLAYWRIGHT_BASE_URL="http://localhost:5173"- Set up the database:
# Apply schema
PGPASSWORD=postgres psql -h localhost -U postgres -p 5434 -d transcripts -f ../sql/schema.sql
# Seed test data
npm run seed-db- Start the backend API:
cd ..
uvicorn app.main:app --reload --port 8000- Start the frontend dev server:
cd frontend
npm run dev- Run E2E tests:
cd e2e
# Run all tests
npm test
# Run in headed mode (see browser)
npm run test:headed
# Run specific test file
npx playwright test tests/auth.spec.ts
# Run tests in a specific browser
npm run test:chromium
npm run test:firefox
npm run test:webkit
# Run mobile tests
npm run test:mobile
# Run only critical tests
npm run test:criticalRun tests in debug mode with Playwright Inspector:
npm run test:debugOr debug a specific test:
npx playwright test tests/auth.spec.ts --debugRun tests in interactive UI mode:
npm run test:uiThis opens a GUI where you can:
- See all tests
- Run individual tests
- Watch tests execute
- Time-travel through test steps
- Inspect DOM snapshots
e2e/
├── fixtures/ # Test data and utilities
│ ├── test-data.ts # Sample test data (users, jobs, videos)
│ ├── db-seeder.ts # Database seeding utility
│ └── seed-database.ts # CLI script for seeding
├── tests/ # Test specifications
│ ├── auth.spec.ts # Authentication tests
│ ├── job-creation.spec.ts # Job creation tests
│ ├── search.spec.ts # Search functionality tests
│ ├── export.spec.ts # Export feature tests
│ ├── billing.spec.ts # Billing and quota tests
│ └── error-handling.spec.ts # Error scenarios
├── playwright.config.ts # Playwright configuration
├── package.json # E2E dependencies
└── README.md # This file
import { test, expect } from '@playwright/test';
test.describe('Feature Name', () => {
test.beforeEach(async ({ page, context }) => {
// Setup: authenticate, mock APIs, etc.
});
test('should do something', async ({ page }) => {
// Navigate
await page.goto('/path');
// Interact
await page.locator('button').click();
// Assert
await expect(page.locator('text=Success')).toBeVisible();
});
});Mock API endpoints to control test data:
await page.route('**/api/search*', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
results: [/* test data */],
total: 1,
}),
});
});// Add session cookie
await context.addCookies([
{
name: 'tc_session',
value: 'mock-session-token',
domain: 'localhost',
path: '/',
httpOnly: true,
secure: false,
sameSite: 'Lax',
},
]);
// Mock auth endpoint
await page.route('**/api/auth/me', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: 'user-id',
email: 'user@example.com',
name: 'Test User',
plan: 'free',
}),
});
});Test mobile viewports:
test.describe('Mobile Tests', () => {
test.use({ viewport: { width: 375, height: 667 } });
test('should work on mobile', async ({ page }) => {
await page.goto('/');
// Use .tap() instead of .click() for mobile interactions
await page.locator('button').tap();
});
});Test file downloads:
const downloadPromise = page.waitForEvent('download');
await page.locator('button:has-text("Download")').click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toContain('.srt');
// Optionally verify file contents
const path = await download.path();
const content = fs.readFileSync(path, 'utf8');
expect(content).toContain('expected content');- Services not running: Ensure API and frontend are running
- Database not seeded: Run
npm run seed-dbin the e2e directory - Port conflicts: Check if ports 8000 (API) and 5173 (frontend) are available
-
Timing issues: Use proper waiting mechanisms:
// Good: Wait for element await expect(page.locator('text=Result')).toBeVisible(); // Bad: Fixed timeout await page.waitForTimeout(1000); // Avoid unless necessary
-
Network delays: Increase timeouts for slow operations:
await expect(page.locator('text=Result')).toBeVisible({ timeout: 10000 });
-
Race conditions: Use proper selectors and wait for elements
Run tests with --debug to use the inspector:
npx playwright test --debugFeatures:
- Step through tests
- Inspect element locators
- View console logs
- Record new tests
View traces for failed tests:
npx playwright show-trace test-results/path-to-trace.zipFeatures:
- Timeline of test execution
- DOM snapshots at each step
- Network activity
- Console logs
- Screenshots
Configured to capture on failure:
- Screenshots:
test-results/*/screenshot.png - Videos:
test-results/*/video.webm
View the HTML report:
npm run show-reportTests run automatically on:
- Pull requests (critical tests only)
- Push to main (full suite)
- Nightly schedule (full suite + mobile)
- Manual trigger
-
Critical Tests on PRs: Fast subset (~5-10 min)
- Authentication
- Job creation
- Search
-
Full Suite on Main: All tests, multiple browsers (~20-30 min)
- Chromium
- Firefox
- WebKit
-
Nightly: Full suite + mobile tests (~30-45 min)
- All browsers
- Mobile viewports
- Go to the PR or commit in GitHub
- Click "Details" on the E2E Tests check
- View job logs and artifacts
- Download Playwright report from artifacts
-
Use meaningful test descriptions:
test('should show validation error for invalid YouTube URL', async ({ page }) => { // ... });
-
Group related tests:
test.describe('Authentication Flow', () => { // Related auth tests });
-
Mock external services: Don't depend on real YouTube API, Stripe, etc.
-
Use data-testid for stable selectors:
await page.locator('[data-testid="submit-button"]').click();
-
Test user workflows, not implementation details
-
Clean up test data: Use fixtures and cleanup hooks
-
Run tests in parallel: Playwright does this by default
-
Use proper assertions:
// Good await expect(page.locator('text=Success')).toBeVisible(); // Bad expect(await page.locator('text=Success').isVisible()).toBe(true);
-
Don't use fixed timeouts: Use
waitForSelector,toBeVisible, etc. -
Don't test implementation details: Test user-facing behavior
-
Don't make tests interdependent: Each test should be independent
-
Don't use real credentials: Use test data and mocks
-
Don't ignore flaky tests: Fix them or mark as known issues
-
Don't hardcode URLs: Use configuration
-
Don't write tests that are too specific: They break easily
-
Reuse authentication state:
test.use({ storageState: 'auth.json' });
-
Run tests in parallel: Default with Playwright
-
Use
test.skip()for WIP tests: Don't comment them out -
Optimize beforeEach: Only set up what's needed
-
Use shallow rendering when possible: Mock deeply nested components
- ✅ All critical user flows tested
- ✅ Authentication and authorization
- ✅ Job creation and processing
- ✅ Search functionality
- ✅ Export features
- ✅ Billing and quotas
- ✅ Error handling
- ✅ Mobile responsiveness
- ✅ Cross-browser compatibility
# Check if PostgreSQL is running
psql -h localhost -U postgres -p 5434 -d transcripts -c "SELECT 1"
# Reset database
PGPASSWORD=postgres psql -h localhost -U postgres -p 5434 -d transcripts -f ../sql/schema.sql
cd e2e && npm run seed-db# Check if API is running
curl http://localhost:8000/health
# Restart API
cd .. && uvicorn app.main:app --reload --port 8000# Check if frontend is running
curl http://localhost:5173
# Restart frontend
cd frontend && npm run dev# Reinstall browsers
npx playwright install --with-deps
# Or install specific browser
npx playwright install --with-deps chromium- Playwright Documentation
- Playwright Best Practices
- Playwright API Reference
- Writing Tests Guide
- Debugging Guide
When adding new features:
- Write E2E tests for critical user paths
- Update this guide if introducing new patterns
- Ensure tests pass locally before pushing
- Keep tests fast and reliable
- Document any special setup requirements
If you encounter issues or have questions:
- Check this guide
- Review existing tests for patterns
- Check Playwright documentation
- Ask in the team chat
- Open an issue on GitHub