This guide covers setting up and running Playwright E2E tests for the myimageupscaler.com application.
- Node.js 18+
- Yarn package manager
- Supabase project (for billing tests)
npx playwright install chromium# Run all E2E tests (headless)
yarn test:e2e
# Run all tests (E2E + API)
yarn test:all
# Full verification (TypeScript + Lint + Tests)
yarn verifyThe authentication tests (auth.e2e.spec.ts) run without any environment configuration:
yarn test:e2eBilling tests require access to Supabase Admin API to create/cleanup test users.
- Go to Supabase Dashboard
- Select your project
- Navigate to Settings → API
- Under "Project API keys", copy the service_role key (NOT the anon key)
# Copy the example file
cp .env.prod.example .env.prodEdit .env.prod and add your keys:
# Supabase - Get from: Supabase Dashboard > Settings > API
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Also ensure .env has the public URL
# NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.coyarn test:e2eWith the environment configured, billing tests will run instead of being skipped.
| Command | Description |
|---|---|
yarn test:e2e |
Run E2E browser tests (chromium, headless) |
yarn test:api |
Run API tests only |
yarn test:all |
Run all Playwright tests |
yarn test:e2e:ui |
Open interactive Playwright UI (requires display) |
yarn test:e2e:debug |
Debug mode with browser DevTools |
yarn test:e2e:report |
Show HTML test report |
yarn verify |
Full validation (tsc + lint + all tests) |
tests/
├── e2e/ # Browser E2E tests (*.e2e.spec.ts)
│ ├── auth.e2e.spec.ts # Authentication flows
│ └── billing.e2e.spec.ts # Billing/subscription flows
├── api/ # API tests (*.api.spec.ts)
│ ├── health.api.spec.ts
│ ├── checkout.api.spec.ts
│ └── webhooks.api.spec.ts
├── pages/ # Page Object Models
│ ├── BasePage.ts
│ ├── LoginPage.ts
│ ├── BillingPage.ts
│ └── PricingPage.ts
└── helpers/ # Test utilities
├── auth.ts # Auth fixtures
├── test-data-manager.ts
└── checkout-mock.ts
Tests use the Page Object Model pattern for maintainability:
// tests/pages/LoginPage.ts
export class LoginPage extends BasePage {
async openLoginModal() {
await this.page.locator('header').waitFor({ state: 'visible' });
await this.page.getByRole('button', { name: /sign in/i }).click();
await expect(this.page.locator('div[role="dialog"]')).toBeVisible();
}
async login(email: string, password: string) {
await this.openLoginModal();
await this.page.getByLabel(/email/i).fill(email);
await this.page.getByLabel(/password/i).fill(password);
await this.page.getByRole('button', { name: /sign in/i }).click();
}
}Usage in tests:
// tests/e2e/auth.e2e.spec.ts
import { LoginPage } from '../pages/LoginPage';
test('should show login form', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto('/');
await loginPage.openLoginModal();
await loginPage.assertModalVisible();
});// tests/pages/MyPage.ts
import { BasePage } from './BasePage';
export class MyPage extends BasePage {
async doSomething() {
// Use accessibility-driven selectors
await this.page.getByRole('button', { name: /submit/i }).click();
}
}// tests/e2e/my-feature.e2e.spec.ts
import { test, expect } from '@playwright/test';
import { MyPage } from '../pages/MyPage';
test.describe('My Feature', () => {
test('should work correctly', async ({ page }) => {
const myPage = new MyPage(page);
await myPage.goto('/my-route');
await myPage.doSomething();
await expect(page.getByText('Success')).toBeVisible();
});
});Prefer accessibility-driven selectors (resilient to UI changes):
// Good - accessibility selectors
page.getByRole('button', { name: /submit/i });
page.getByLabel('Email');
page.getByText('Welcome');
// Avoid - brittle selectors
page.locator('.btn-primary');
page.locator('#submit-btn');
page.locator('div > span.text');If billing tests show as skipped (-):
- 1 › Billing System E2E Tests › should display free user state
Cause: Missing SUPABASE_SERVICE_ROLE_KEY in .env.prod
Solution: Follow the Billing Tests setup above.
Error: browserType.launch: Executable doesn't exist
Solution:
npx playwright install chromiumPort 3000 is in use by an unknown process
Solution:
# Kill processes on port 3000
lsof -ti:3000 | xargs kill -9
# Or let Playwright use the existing server
# (playwright.config.ts has reuseExistingServer: true for local dev)TimeoutError: page.waitForSelector: Timeout 15000ms exceeded
Possible causes:
- Slow compilation - increase timeout in
playwright.config.ts - Selector changed - update Page Object
- Server not ready - check webServer configuration
For CI environments, set environment variables as secrets:
# GitHub Actions example
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}Run tests in CI:
CI=true yarn test:e2e- NEVER commit
.env.prodto version control SUPABASE_SERVICE_ROLE_KEYbypasses Row Level Security (RLS)- Only use service role key server-side (API routes, tests)
- Rotate keys if compromised