E2E Test Optimizer
Quick Start
This skill helps fix three critical E2E test issues:
-
Anti-pattern removal: Replace arbitrary waitForTimeout with smart waits
-
Test sharding: Enable parallel test execution in CI
-
Mock optimization: Reduce mock setup overhead
When to Use
-
E2E tests timing out or failing due to arbitrary waits
-
CI execution time exceeds 10 minutes
-
Need test parallelization for faster feedback
-
Test flakiness from race conditions
Smart Wait Patterns
Navigation Waits
// ❌ Arbitrary wait await page.click('[data-testid="nav-dashboard"]'); await page.waitForTimeout(1000);
// ✅ Wait for specific element await page.click('[data-testid="nav-dashboard"]'); await expect(page.getByTestId('dashboard-content')).toBeVisible({ timeout: 5000, });
State Transition Waits
// ❌ Arbitrary wait after action await page.getByRole('button', { name: 'Generate' }).click(); await page.waitForTimeout(2000);
// ✅ Wait for loading state completion await page.getByRole('button', { name: 'Generate' }).click(); await expect(page.getByTestId('loading-spinner')).not.toBeVisible(); await expect(page.getByTestId('result')).toBeVisible();
Network Waits
// ❌ Arbitrary wait after submit await page.getByRole('button', { name: 'Save' }).click(); await page.waitForTimeout(1500);
// ✅ Wait for network idle or success message await page.getByRole('button', { name: 'Save' }).click(); await page.waitForLoadState('networkidle'); // OR await expect(page.getByText('Saved successfully')).toBeVisible();
Test Sharding Setup
Add to .github/workflows/ci.yml :
e2e-tests: name: 🧪 E2E Tests [Shard ${{ matrix.shard }}/3] runs-on: ubuntu-latest timeout-minutes: 30 needs: build-and-test strategy: fail-fast: false matrix: shard: [1, 2, 3] steps: - name: Run Playwright tests run: pnpm exec playwright test --shard=${{ matrix.shard }}/3 env: CI: true
Expected improvement: 60-65% faster (27 minutes → 9-10 minutes)
Detection Commands
Find all waitForTimeout usage
grep -r "waitForTimeout" tests/specs/*.spec.ts
Count per file
grep -c "waitForTimeout" tests/specs/*.spec.ts
Standard Test Structure
import { test, expect } from '@playwright/test'; import { setupGeminiMock } from '../utils/mock-ai-gateway';
test.describe('Feature Name', () => { test.beforeEach(async ({ page }) => { await setupGeminiMock(page); await page.goto('/'); await page.waitForLoadState('networkidle'); });
test('should perform action', async ({ page }) => { // Navigate await page.getByTestId('nav-target').click(); await expect(page.getByTestId('target-page')).toBeVisible();
// Interact
await page.getByRole('button', { name: 'Action' }).click();
await expect(page.getByTestId('loading')).not.toBeVisible();
// Assert
await expect(page.getByTestId('result')).toBeVisible();
}); });
Element Selection Priority
// ✅ Best: data-testid (stable, explicit) await page.getByTestId('project-card');
// ✅ Good: role + name (semantic, accessible) await page.getByRole('button', { name: 'Create' });
// ⚠️ OK: text (can break with i18n) await page.getByText('Dashboard');
// ❌ Avoid: CSS selectors (brittle) await page.locator('.project-card > div.title');
Optimization Workflow
Phase 1: Analysis
-
Scan test files for waitForTimeout
-
Count occurrences per file
-
Prioritize files by occurrence count
Phase 2: Fix Anti-Patterns
-
Start with highest-priority file
-
Replace each waitForTimeout with smart wait
-
Run tests after each file: playwright test path/to/file.spec.ts
-
Commit per-file changes
Phase 3: Implement Sharding
-
Update CI workflow with matrix strategy
-
Test locally: playwright test --shard=1/3
-
Push and monitor all 3 shards in CI
Phase 4: Validation
-
Verify all tests pass
-
Confirm execution time < 10 minutes
-
Check shard balance (±2 min variance acceptable)
Common Issues
Tests still timing out after fix
- Increase timeout on slow operations: expect(...).toBeVisible({ timeout: 10000 })
Unbalanced shards (one takes much longer)
- Use --grep to manually distribute heavy tests across shards
Mock setup still slow
- Implement global browser warm-up (see MOCK-OPTIMIZATION-GUIDE.md in tests/docs)
Success Criteria
-
Zero waitForTimeout calls in active tests
-
CI execution time < 10 minutes
-
All shards complete within ±2 minutes of each other
-
100% test pass rate (no flaky tests)
References
See tests/docs/ for detailed guides:
-
MOCK-OPTIMIZATION-GUIDE.md - Mock performance patterns
-
MOCK-PERFORMANCE-ANALYSIS.md - Optimization results
External documentation:
-
Playwright Best Practices: https://playwright.dev/docs/best-practices
-
Test Sharding: https://playwright.dev/docs/test-sharding