--- description: Initialize Playwright E2E testing for Tanstack Start projects with Cloudflare Workers-specific configuration --- # Playwright Test Setup Command Configure Playwright for end-to-end testing in Tanstack Start projects deployed to Cloudflare Workers. Sets up test infrastructure, accessibility testing, and Workers-specific patterns. ## Introduction Senior QA Engineer specializing in Playwright setup for Tanstack Start + Cloudflare Workers applications This command initializes a complete Playwright testing setup optimized for: - Tanstack Start (React + TanStack Router) - Cloudflare Workers deployment - Server function testing - Cloudflare bindings (KV, D1, R2, DO) - Accessibility testing - Performance monitoring ## Prerequisites - Tanstack Start project initialized - Cloudflare Workers configured (wrangler.jsonc) - Node.js 18+ - npm/pnpm/yarn ## Main Tasks ### 1. Verify Project Setup Ensure this is a Tanstack Start project before installing Playwright. ```bash # Check for Tanstack Start if ! grep -q "@tanstack/start" package.json; then echo "❌ Not a Tanstack Start project" echo "This command requires Tanstack Start." exit 1 fi # Check for wrangler config if [ ! -f "wrangler.jsonc" ] && [ ! -f "wrangler.toml" ]; then echo "⚠️ No wrangler config found" echo "Playwright will be configured, but Cloudflare bindings tests may not work." fi ``` ### 2. Install Playwright Dependencies ```bash # Install Playwright and dependencies pnpm add -D @playwright/test @axe-core/playwright # Install browsers npx playwright install --with-deps chromium firefox webkit ``` ### 3. Create Playwright Configuration **File**: `playwright.config.ts` ```typescript import { defineConfig, devices } from '@playwright/test' export default defineConfig({ testDir: './e2e', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: [ ['html'], ['list'], process.env.CI ? ['github'] : ['list'], ], use: { baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, }, { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] }, }, { name: 'Mobile Safari', use: { ...devices['iPhone 12'] }, }, ], webServer: { command: 'pnpm dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, timeout: 120 * 1000, }, }) ``` ### 4. Create Directory Structure ```bash mkdir -p e2e/{routes,server-functions,components,auth,accessibility,performance,visual,fixtures} ``` ### 5. Create Example Tests **File**: `e2e/example.spec.ts` ```typescript import { test, expect } from '@playwright/test' test.describe('Example Tests', () => { test('home page loads', async ({ page }) => { await page.goto('/') await expect(page).toHaveTitle(/.*/) await expect(page.locator('body')).toBeVisible() }) test('has no console errors', async ({ page }) => { const errors: string[] = [] page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()) } }) await page.goto('/') expect(errors).toHaveLength(0) }) }) ``` **File**: `e2e/accessibility/home.spec.ts` ```typescript import { test, expect } from '@playwright/test' import AxeBuilder from '@axe-core/playwright' test.describe('Accessibility', () => { test('home page has no a11y violations', async ({ page }) => { await page.goto('/') const accessibilityScanResults = await new AxeBuilder({ page }) .analyze() expect(accessibilityScanResults.violations).toEqual([]) }) }) ``` **File**: `e2e/performance/metrics.spec.ts` ```typescript import { test, expect } from '@playwright/test' test.describe('Performance', () => { test('measures page load time', async ({ page }) => { const startTime = Date.now() await page.goto('/') await page.waitForLoadState('networkidle') const loadTime = Date.now() - startTime console.log(`Page load time: ${loadTime}ms`) // Cloudflare Workers should load fast expect(loadTime).toBeLessThan(1000) }) test('measures TTFB', async ({ page }) => { await page.goto('/') const timing = await page.evaluate(() => JSON.parse(JSON.stringify( performance.getEntriesByType('navigation')[0] )) ) const ttfb = timing.responseStart - timing.requestStart console.log(`TTFB: ${ttfb}ms`) // Time to First Byte should be fast on Workers expect(ttfb).toBeLessThan(200) }) }) ``` ### 6. Create Test Fixtures **File**: `e2e/fixtures/test-users.ts` ```typescript export const testUsers = { admin: { email: 'admin@test.com', password: 'admin123', name: 'Admin User', }, regular: { email: 'user@test.com', password: 'user123', name: 'Regular User', }, } ``` ### 7. Update package.json Scripts ```json { "scripts": { "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "test:e2e:debug": "playwright test --debug", "test:e2e:headed": "playwright test --headed", "test:e2e:report": "playwright show-report" } } ``` ### 8. Create .env.test for Cloudflare Bindings **File**: `.env.test` ```bash # Cloudflare Test Environment CLOUDFLARE_ACCOUNT_ID=your-test-account-id CLOUDFLARE_API_TOKEN=your-test-api-token # Test Bindings (separate from production) KV_NAMESPACE_ID=test-kv-namespace-id D1_DATABASE_ID=test-d1-database-id R2_BUCKET_NAME=test-r2-bucket # Test Base URL PLAYWRIGHT_TEST_BASE_URL=http://localhost:3000 ``` ### 9. Create GitHub Actions Workflow (Optional) **File**: `.github/workflows/e2e.yml` ```yaml name: E2E Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node uses: actions/setup-node@v3 with: node-version: 18 - name: Install dependencies run: pnpm install - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run Playwright tests run: pnpm test:e2e env: CLOUDFLARE_ACCOUNT_ID: ${ secrets.CLOUDFLARE_ACCOUNT_ID} CLOUDFLARE_API_TOKEN: ${ secrets.CLOUDFLARE_API_TOKEN} - name: Upload Playwright Report uses: actions/upload-artifact@v3 if: always() with: name: playwright-report path: playwright-report/ retention-days: 30 ``` ### 10. Create Testing Guide **File**: `e2e/README.md` ```markdown # E2E Testing Guide ## Running Tests ```bash # Run all tests pnpm test:e2e # Run with UI mode pnpm test:e2e:ui # Run specific test file pnpm test:e2e e2e/routes/home.spec.ts # Run in headed mode (see browser) pnpm test:e2e:headed # Debug mode pnpm test:e2e:debug ``` ## Test Organization - `routes/` - Tests for TanStack Router routes - `server-functions/` - Tests for server functions - `components/` - Tests for shadcn/ui components - `auth/` - Authentication flow tests - `accessibility/` - Accessibility tests (axe-core) - `performance/` - Performance and load time tests - `visual/` - Visual regression tests - `fixtures/` - Test data and helpers ## Best Practices 1. **Test user behavior, not implementation** - Focus on what users see and do - Avoid testing internal state 2. **Use data-testid for stable selectors** ```tsx ``` 3. **Test with real Cloudflare bindings** - Use test environment bindings - Don't mock KV, D1, R2, DO 4. **Run accessibility tests on every page** - Zero violations policy - Use @axe-core/playwright 5. **Monitor performance metrics** - Cold start < 500ms - TTFB < 200ms - Bundle size < 200KB ``` ### 11. Add .gitignore Entries ```bash # Add to .gitignore cat >> .gitignore << 'EOF' # Playwright /test-results/ /playwright-report/ /playwright/.cache/ EOF ``` ### 12. Validation **Task playwright-testing-specialist(verify setup)**: - Confirm Playwright installed - Verify browser binaries downloaded - Check test directory structure - Validate configuration file - Run example test to ensure setup works ```bash # Run validation pnpm test:e2e --reporter=list # Should see: # ✓ example.spec.ts:5:3 › Example Tests › home page loads # ✓ accessibility/home.spec.ts:6:3 › Accessibility › home page has no a11y violations # ✓ performance/metrics.spec.ts:6:3 › Performance › measures page load time ``` ## Output After running `/es-test-setup`, you will have: ✅ Playwright installed with all browsers ✅ Test directory structure created ✅ Configuration file (playwright.config.ts) ✅ Example tests (routes, accessibility, performance) ✅ Test fixtures and helpers ✅ npm scripts for running tests ✅ CI/CD workflow template ✅ Testing guide documentation ## Next Steps 1. **Run example tests**: ```bash pnpm test:e2e ``` 2. **Generate tests for your routes**: ```bash /es-test-gen /users/$id ``` 3. **Add tests to your workflow**: - Write tests as you build features - Run tests before deployment - Monitor test results in CI/CD ## Troubleshooting ### Issue: "Cannot find module '@playwright/test'" **Solution**: ```bash pnpm install npx playwright install ``` ### Issue: "Browser not found" **Solution**: ```bash npx playwright install --with-deps ``` ### Issue: "Tests timing out" **Solution**: Increase timeout in `playwright.config.ts`: ```typescript export default defineConfig({ timeout: 60 * 1000, // 60 seconds per test // ... }) ``` ### Issue: "Accessibility violations found" **Solution**: Fix the violations! Playwright will show you exactly what's wrong: ``` Expected: [] Received: [ { "id": "color-contrast", "impact": "serious", "description": "Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds", "nodes": [...] } ] ``` ## Resources - **Playwright Docs**: https://playwright.dev - **Axe Accessibility**: https://github.com/dequelabs/axe-core-npm/tree/develop/packages/playwright - **Cloudflare Testing**: https://developers.cloudflare.com/workers/testing/ - **Best Practices**: https://playwright.dev/docs/best-practices ## Success Criteria ✅ Playwright installed and configured ✅ Example tests passing ✅ Accessibility testing enabled ✅ Performance monitoring setup ✅ CI/CD workflow ready ✅ Team trained on testing practices