| name | e2e-testing-for-webapps |
| description | E2E testing for Next.js + Playwright + Supabase. OAuth bypass via test users, interactive debugging, visual QA. Use when: E2E, Playwright, visual regression, Electron testing. |
E2E Testing for Web & Electron Apps
Two modes: Batch testing (CI/test suites) and Interactive debugging (persistent browser sessions for iterative QA). Both share the same QA methodology.
Stack: Next.js 16 + Playwright + Supabase Auth + agent-native vision
Architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ E2E TESTING ARCHITECTURE │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ MODE 1: BATCH (CI / Test Suites) │
│ ═══════════════════════════════ │
│ Playwright Test runner → auth setup → test specs → reports │
│ ✓ Headless CI ✓ Parallel workers ✓ Retries ✓ Artifacts │
│ │
│ MODE 2: INTERACTIVE (Debugging / Iterative QA) │
│ ═══════════════════════════════════════════════ │
│ Persistent browser session → live reload → functional QA → visual QA │
│ ✓ Desktop/Mobile/Electron ✓ Session reuse ✓ Screenshot analysis │
│ │
│ SHARED INFRASTRUCTURE │
│ ═════════════════════ │
│ 1. Auth bypass: Supabase test users with email/password (no OAuth) │
│ 2. Console monitoring: capture runtime, network, hydration errors │
│ 3. Page Objects: BasePage → DashboardPage, SettingsPage, etc. │
│ 4. AI visual analysis: Screenshots → agent-native vision → QA │
│ 5. Diagnostic tools: DOM health check, layout snapshot diff, styles │
│ 6. Failure injection: network, input stress, state corruption │
│ 7. QA methodology: inventory → functional QA → visual QA → signoff │
│ │
└──────────────────────────────────────────────────────────────────────────┘
Quick Start: Batch Testing
bun add -D @playwright/test && bunx playwright install chromium
bun scripts/provision-e2e-test-users.ts --user=primary
E2E_TEST_EMAIL=test@app.test E2E_TEST_PASSWORD=xxx bun run test:e2e:prod
Quick Start: Interactive Debugging
test -f package.json || npm init -y
npm install playwright
npx playwright install chromium
node -e "import('playwright').then(() => console.log('ready'))"
Then launch a browser and start testing — see INTERACTIVE-SESSIONS.md.
The Google OAuth Bypass
Problem: Google blocks automated logins (CAPTCHA, headless detection).
Solution: Create test users with email/password auth in Supabase — same app, different auth method.
const { data, error } = await supabase.auth.signInWithPassword({
email: process.env.E2E_TEST_EMAIL,
password: process.env.E2E_TEST_PASSWORD,
});
await context.addCookies([
{ name: 'sb-access-token', value: data.session.access_token, domain: '...', ... },
]);
Why .test TLD? IANA-reserved, never resolves — test emails can't leak to real inboxes.
Deep dive: AUTHENTICATION.md
QA Inventory Methodology
Before testing, build a coverage list from three sources:
- User's requested requirements — what was asked for
- Implemented features/behaviors — what you actually built
- Claims in the final response — what you intend to sign off on
Everything in any of those three sources must map to at least one QA check.
For each item, note:
- The intended functional check (user input → expected result)
- The specific state where visual check must happen
- The evidence to capture (screenshot, assertion)
Add at least 2 exploratory/off-happy-path scenarios that could expose fragile behavior.
Update the inventory during testing if exploration reveals additional controls, states, or claims.
Workflow
Batch Mode Phases
| Phase | Steps |
|---|
| 1. Setup | Install Playwright, create test users, configure storage state |
| 2. Implementation | Build Page Objects, add console monitoring, capture screenshots |
| 3. Enhancement | Add visual analysis, screenshot capture, HTML/JSON reports |
| 4. CI Integration | Add test:e2e script, GitHub Actions workflow, artifact upload |
Interactive Mode Loop
- Build QA inventory from the three sources above
- Bootstrap persistent browser session (once)
- Start/confirm dev server
- Launch runtime (web or Electron), keep handles alive
- Edit-Reload-Verify micro-loop for each component:
- Make one change → reload → snapshot diff → DOM health check → fix issues → screenshot → verify
- Run functional QA with real user input (use
.click(), not page.evaluate())
- Run separate visual QA pass
- Run breakpoint sweep for responsive verification
- Run failure injection pass (network failures, input stress, state corruption)
- Verify viewport fit, capture evidence screenshots
- Update inventory if exploration reveals new items
- Repeat 5-11 until signoff criteria met
- Clean up session when task is finished
Deep dive: Edit-Reload-Verify loop → SYSTEMATIC-TESTING.md, DOM health check → DIAGNOSTIC-TOOLS.md
Deep dive: INTERACTIVE-SESSIONS.md
Functional QA
- Use real user controls for signoff:
.click(), .fill(), .press() — NOT page.evaluate()
- Interact via Playwright action methods which simulate real mouse/keyboard and respect visibility, overlapping elements, and event bubbling —
page.evaluate() bypasses all of this
- Verify visible results, not just internal state
- Cover every control in the QA inventory at least once
- For stateful toggles: test the full cycle (initial → changed → returned to initial)
- Test interactive states (hover, focus, disabled, error, empty, overflow) — see DIAGNOSTIC-TOOLS.md
- Exploratory pass (30-90 seconds) using normal input, not only the happy path
- If exploratory pass reveals new states/controls, add to inventory
Visual QA (Separate Pass)
- Each user-visible claim needs a matching visual check + reviewed screenshot
- Inspect initial viewport before scrolling
- Check all required regions, not just the main interaction surface
- Look for: clipping, overflow, distortion, weak contrast, broken layering, alignment problems
- Judge aesthetic quality as well as correctness
- For dynamic visuals, inspect long enough to judge stability — don't rely on a single screenshot
- Before signoff, ask: "What visible defect would most embarrass this result?"
Deep dive: VISUAL-QA.md
Signoff Criteria
All three must pass independently — one does not imply the others:
- Functional correctness — user input paths work, QA inventory covered
- Viewport fit — intended initial view visible without unintended clipping/scrolling
- Visual quality — UI is coherent, not aesthetically weak for the task
- Failure resilience (optional, recommended) — app handles network errors, input stress, and state corruption gracefully — see FAILURE-INJECTION.md
Include brief negative confirmation of defect classes checked and not found.
Test User Tiers
| Type | Email | Tier | Purpose |
|---|
primary | e2e-test@app.test | Pro | Main tests, full features |
free | e2e-free@app.test | Free | Paywall, limitations |
premium | e2e-premium@app.test | Premium | All features unlocked |
fresh | e2e-new@app.test | None | Onboarding, empty states |
admin | e2e-admin@app.test | Admin | Admin panel tests |
Key Configuration
export default defineConfig({
testDir: './e2e',
timeout: 60000,
retries: 2,
use: {
baseURL: 'https://your-app.com',
trace: 'retain-on-failure',
screenshot: 'on',
video: 'retain-on-failure',
actionTimeout: 30000,
navigationTimeout: 60000,
},
projects: [
{ name: 'auth-setup', testMatch: /auth\.global-setup\.ts/ },
{
name: 'authenticated',
dependencies: ['auth-setup'],
use: { storageState: '.auth/user.json', ...devices['Desktop Chrome'] },
},
],
});
Console Error Categories
| Category | Patterns | Action |
|---|
hydration | hydrat, server.*different.*client | Fix SSR mismatch |
runtime | TypeError, ReferenceError | Fix JS error |
network | net::ERR, fetch.*failed | Check API/CORS |
react | Warning:, useEffect | Fix hook issue |
security | CSP, Refused to | Fix CSP policy |
Deep dive: CONSOLE-MONITORING.md
Page Object Pattern
export class DashboardPage extends BasePage {
static readonly PATH = '/portfolio';
readonly healthScoreWidget = this.page.locator('[data-testid="health-score"]');
async goto() { await super.goto(DashboardPage.PATH); }
async getHealthScore(): Promise<number | null> {
const text = await this.healthScoreWidget.textContent();
return text ? parseInt(text.match(/(\d+)/)?.[1] ?? '', 10) : null;
}
}
Deep dive: PAGE-OBJECTS.md
AI Visual Analysis
The agent IS the vision model. Capture screenshots with Playwright, emit or save them, and the agent analyzes them directly using its built-in multimodal capabilities. No external API calls needed.
await codex.emitImage({ bytes: await page.screenshot({ type: "jpeg", quality: 85, scale: "css" }), mimeType: "image/jpeg" });
await page.screenshot({ path: '/tmp/visual-check.png' });
Deep dive: AI-VISUAL-ANALYSIS.md
Running Tests
bun run test:e2e
bun run test:e2e:prod --headed
bun run test:e2e e2e/tests/dashboard.spec.ts
bun run test:e2e:prod
await page.goto('http://127.0.0.1:3000');
await page.reload({ waitUntil: 'domcontentloaded' });
await page.screenshot({ type: 'jpeg', quality: 85 });
Validation Checklist
Reference Index
By Task
By Topic
| Topic | Reference |
|---|
| Google OAuth bypass, Supabase test users, provisioning | AUTHENTICATION.md |
| Persistent sessions, Electron, mobile, reload/relaunch | INTERACTIVE-SESSIONS.md |
| QA inventory, functional/visual QA, viewport fit, signoff | VISUAL-QA.md |
| CSS normalization, model-bound screenshots, click helpers | SCREENSHOTS.md |
| Page Object Model, BasePage, locator strategies, fixtures | PAGE-OBJECTS.md |
| Browser console capture, error categorization, filtering | CONSOLE-MONITORING.md |
| Agent-native visual analysis, structured QA, severity thresholds | AI-VISUAL-ANALYSIS.md |
| HTML/JSON reports, CI artifacts, screenshot management | REPORTING.md |
| LLM diff, SoM overlays, ARIA, stabilization, Test Agents, CI QA | ADVANCED-TECHNIQUES.md |
| CLI commands, config snippets, failure modes | QUICK-REFERENCE.md |
| DOM health check, layout snapshot diff, computed styles, breakpoint sweep, state triggers | DIAGNOSTIC-TOOLS.md |
| Human-like interaction, Edit-Reload-Verify loop, state matrix, state catalog |
Tools & Scripts
| Tool | Purpose |
|---|
scripts/provision-e2e-test-users.ts | Create test users in Supabase |
scripts/reset-e2e-test-user.ts | Reset user to known seed state |
scripts/validate-e2e.sh | Validate E2E setup |