# E2E Tests with Playwright End-to-end testing infrastructure for the Custom PHP Framework using Playwright. ## Overview Comprehensive browser-based E2E tests for: - **Critical User Journeys** - Homepage, authentication, core workflows - **LiveComponents Functionality** - Real-time updates, form validation, fragment rendering - **Integration Scenarios** - Cross-browser compatibility, performance, security ## Test Coverage ### Critical Paths (`critical-paths/`) #### Homepage Tests (`critical-paths/homepage.spec.ts`) - Page load and accessibility - Valid HTML structure and meta tags - Navigation functionality - JavaScript error detection - Security headers verification - Responsive design (mobile viewport) - ARIA landmarks and accessibility - CSS and asset loading ### LiveComponents Tests (`live-components/`) #### Form Validation (`live-components/form-validation.spec.ts`) - Real-time validation error display - Email format validation - Validation error clearing - Submit prevention with errors - Required field handling - Max length validation - Character counter functionality #### Real-time Updates (`live-components/real-time-updates.spec.ts`) - SSE (Server-Sent Events) connections - HTMX request handling - DOM updates without page reload - Loading states during updates - Form data preservation during partial updates - WebSocket connections (if present) - Connection error handling ### Legacy LiveComponents Tests #### Fragment Rendering Tests (`livecomponents/fragment-rendering.spec.js`) **Basic Functionality**: - Single fragment patching without full re-render - Multiple fragment updates simultaneously - Nested fragment updates - Fallback to full render when fragments not specified **State Preservation**: - Focus state preservation during fragment updates - Scroll position preservation - Selection range preservation in text inputs - Event listener preservation on updated elements **Performance**: - Fragment rendering speed vs full HTML rendering - Network payload size reduction (fragment vs full) - Batch update optimization - Rapid successive updates handling **Edge Cases**: - Empty fragments - Very large fragments (1000+ items) - Special characters in fragment content - Whitespace and formatting changes - Data attribute updates - Missing fragment error handling ## Prerequisites ### System Dependencies Playwright requires certain system libraries. Install them with: ```bash # Option 1: Using Playwright's installer (recommended) sudo npx playwright install-deps # Option 2: Using apt directly sudo apt-get install \ libatk1.0-0 \ libatk-bridge2.0-0 \ libcups2 \ libxkbcommon0 \ libatspi2.0-0 \ libxcomposite1 \ libxdamage1 \ libxfixes3 \ libxrandr2 \ libgbm1 \ libcairo2 \ libpango-1.0-0 \ libasound2 ``` ### Node.js Dependencies Install Playwright and browsers: ```bash # Install Playwright package (already done via package.json) npm install # Install browser binaries npx playwright install chromium # Chromium only npx playwright install # All browsers (Chromium, Firefox, WebKit) ``` ### Development Server E2E tests require the local development server to be running: ```bash # Start Docker containers and development server make up # Verify server is accessible curl -k https://localhost ``` ## Running Tests ### All E2E Tests ```bash # Run all E2E tests headless npm run test:e2e # Run with UI mode (interactive) npm run test:e2e:ui # Run with headed browsers (see browser windows) npm run test:e2e:headed # Run with debug mode (step through tests) npm run test:e2e:debug ``` ### Specific Test Suites ```bash # Critical path tests only npm run test:e2e:critical # LiveComponents tests only npm run test:e2e:livecomponents # Specific test files npm run test:e2e:homepage npm run test:e2e:validation npm run test:e2e:realtime # Legacy fragment tests npx playwright test livecomponents/fragment-rendering.spec.js ``` ### Filtering Tests ```bash # Run specific test by title npx playwright test --grep "should patch single fragment" # Run tests in specific browser npx playwright test --project=chromium npx playwright test --project=firefox npx playwright test --project=webkit ``` ### Test Output and Reports ```bash # Generate HTML report (automatically opens in browser) npx playwright show-report # View last test run results npx playwright show-report playwright-report ``` ## Configuration ### Playwright Config (`playwright.config.js`) **Key Settings**: - **Base URL**: `https://localhost` (local development server) - **Ignore HTTPS Errors**: Enabled for local SSL certificates - **User-Agent**: Set to avoid firewall blocking - **Timeout**: 30 seconds per test - **Retries**: 2 retries in CI, 0 locally - **Workers**: Parallel execution (1 in CI, auto locally) **Projects Configured**: - Chromium (Desktop) - Firefox (Desktop) - WebKit/Safari (Desktop) - Mobile Chrome (Pixel 5) - Mobile Safari (iPhone 12) **Reporters**: - HTML report: `playwright-report/index.html` - List (console output) - JSON: `playwright-report/results.json` ### Test Environment Tests assume the following URLs are accessible: ``` https://localhost/livecomponents/test/counter https://localhost/livecomponents/test/shopping-cart https://localhost/livecomponents/test/form https://localhost/livecomponents/test/nested-fragments https://localhost/livecomponents/test/performance https://localhost/livecomponents/test/long-list https://localhost/livecomponents/test/data-attributes https://localhost/livecomponents/test/event-listeners https://localhost/livecomponents/test/large-fragment https://localhost/livecomponents/test/special-chars ``` **Note**: These test pages may need to be created in the application. ## Writing New E2E Tests ### Test Structure ```javascript import { test, expect } from '@playwright/test'; test.describe('Feature Name', () => { test.beforeEach(async ({ page }) => { // Navigate to test page await page.goto('https://localhost/path/to/test-page'); // Wait for LiveComponents to initialize await page.waitForFunction(() => window.LiveComponents !== undefined); }); test('should do something', async ({ page }) => { // Arrange: Set up test state const component = page.locator('[data-component-id="component:id"]'); // Act: Perform actions await page.click('[data-action="someAction"]'); await page.waitForTimeout(100); // Wait for fragment update // Assert: Verify results const result = await page.textContent('[data-lc-fragment="result"]'); expect(result).toContain('Expected Value'); }); }); ``` ### Best Practices **1. Use Data Attributes for Selectors**: ```javascript // ✅ Good: Stable selectors await page.click('[data-action="increment"]'); await page.locator('[data-lc-fragment="counter"]'); // ❌ Bad: Brittle selectors await page.click('button.btn-primary'); await page.locator('.counter-display'); ``` **2. Wait for LiveComponents Initialization**: ```javascript // Always wait for LiveComponents to be ready await page.waitForFunction(() => window.LiveComponents !== undefined); ``` **3. Handle Async Updates**: ```javascript // Wait for network request to complete await page.waitForResponse(response => response.url().includes('/live-component/') && response.status() === 200 ); // Or use small timeout for fragment updates await page.waitForTimeout(100); ``` **4. Use Page Object Pattern for Complex Pages**: ```javascript class ShoppingCartPage { constructor(page) { this.page = page; this.addItemButton = page.locator('[data-action="addItem"]'); this.cartItems = page.locator('[data-lc-fragment="cart-items"] .cart-item'); this.cartTotal = page.locator('[data-lc-fragment="cart-total"]'); } async addItem(item) { await this.addItemButton.click(); await this.page.waitForTimeout(100); } async getItemCount() { return await this.cartItems.count(); } } ``` **5. Test Cross-Browser Compatibility**: ```javascript // Use browser-agnostic APIs const userAgent = await page.evaluate(() => navigator.userAgent); // Avoid browser-specific features unless testing for them ``` ## Debugging Tests ### Visual Debugging ```bash # Run with UI mode (recommended) npm run test:e2e:ui # Run with headed browsers npm run test:e2e:headed # Debug specific test npm run test:e2e:debug -- --grep "test name" ``` ### Screenshots and Videos Playwright automatically captures: - **Screenshots**: On test failure - **Videos**: Retained on failure - **Traces**: On first retry View artifacts in `test-results/` directory. ### Console Logs ```javascript // Listen to console messages page.on('console', msg => console.log('Browser log:', msg.text())); // Listen to page errors page.on('pageerror', err => console.error('Page error:', err)); ``` ### Playwright Inspector ```bash # Step through test with debugger npx playwright test --debug # Pause test at specific point await page.pause(); // Add this in test code ``` ## CI/CD Integration ### GitHub Actions Example ```yaml name: E2E Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Install Playwright browsers run: npx playwright install --with-deps chromium - name: Start development server run: make up - name: Run E2E tests run: npm run test:e2e - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: playwright-report path: playwright-report/ ``` ## Troubleshooting ### Common Issues **1. Server not accessible**: ```bash # Verify Docker containers are running docker ps # Check server is responding curl -k https://localhost # Check firewall/SSL settings ``` **2. Tests timing out**: ```javascript // Increase timeout for specific test test('slow operation', async ({ page }) => { test.setTimeout(60000); // 60 seconds // ... }); ``` **3. Flaky tests**: ```javascript // Use waitForSelector with state await page.waitForSelector('[data-lc-fragment="result"]', { state: 'visible', timeout: 5000 }); // Or wait for network to be idle await page.waitForLoadState('networkidle'); ``` **4. Browser launch errors**: ```bash # Install missing dependencies sudo npx playwright install-deps # Use specific browser npx playwright test --project=chromium ``` ### Performance Issues **Slow tests**: - Run fewer browsers: `npx playwright test --project=chromium` - Disable parallel execution: `npx playwright test --workers=1` - Reduce retries: Set `retries: 0` in config **High memory usage**: - Close browsers between test files - Limit workers: `--workers=2` - Use headless mode (default) ## Test Maintenance ### Keeping Tests Updated 1. **Update test pages** when components change 2. **Update selectors** when HTML structure changes 3. **Add new tests** for new features 4. **Remove obsolete tests** for removed features 5. **Review flaky tests** regularly ### Test Quality Metrics Track these metrics for test health: - **Pass rate**: Should be >95% - **Flakiness**: Retry rate should be <5% - **Duration**: Average test duration <30s - **Coverage**: All critical user flows tested ## Resources - [Playwright Documentation](https://playwright.dev) - [Playwright Test API](https://playwright.dev/docs/api/class-test) - [Best Practices](https://playwright.dev/docs/best-practices) - [Debugging Guide](https://playwright.dev/docs/debug) ## Support For issues or questions about E2E tests: 1. Check this README 2. Review existing tests for examples 3. Check Playwright documentation 4. Ask in team chat or create GitHub issue