# E2E Testing Guide This guide covers running, debugging, and maintaining the End-to-End (E2E) test suite for the Cyber Hybrid Hub application. ## Table of Contents - [Overview](#overview) - [Prerequisites](#prerequisites) - [Running Tests](#running-tests) - [Test Structure](#test-structure) - [Writing Tests](#writing-tests) - [Debugging Tests](#debugging-tests) - [CI/CD Integration](#cicd-integration) - [Troubleshooting](#troubleshooting) ## Overview The E2E test suite uses **Playwright** to test the application through the browser, simulating real user interactions. Tests are organized by feature and role (landlord, tenant, admin). ### Test Categories 1. **Authentication** (`tests/e2e/auth/`) - Login/logout flows - Role-based access control (RBAC) - Session management 2. **Landlord Features** (`tests/e2e/landlord/`) - Dashboard - Property management - Applications - Leases - Maintenance requests - Payments 3. **Tenant Features** (`tests/e2e/tenant/`) - Dashboard - Applications - Leases - Maintenance requests - Payments 4. **Error Handling** (`tests/e2e/error-handling/`) - Form validation - Network errors - Unauthorized access - Session expiration - Edge cases ## Prerequisites ### Required Software - **Deno** v2.x: Runtime for the application - **Node.js** v20+: For Playwright CLI - **Playwright**: Browser automation framework ### Installation ```bash # Install Deno (if not already installed) curl -fsSL https://deno.land/install.sh | sh # Install Playwright and browsers cd .playwright-mcp npm install npx playwright install --with-deps chromium ``` ### Environment Setup Set these environment variables (optional, defaults provided): ```bash # Application configuration export PORT=8080 # Port for dev server export E2E_BASE_URL=http://127.0.0.1:8080 # Base URL for tests export E2E_TEST=true # Enable E2E test mode export HOSTNAME_DENO=127.0.0.1 # Deno server hostname ``` ## Running Tests ### Run All Tests ```bash # From project root npx playwright test # Or from .playwright-mcp directory cd .playwright-mcp npx playwright test ``` ### Run Specific Test Files ```bash # Run authentication tests npx playwright test tests/e2e/auth/ # Run specific test file npx playwright test tests/e2e/auth/login.spec.ts # Run tests matching a pattern npx playwright test --grep "login" ``` ### Run Tests in Different Modes ```bash # Run in headed mode (show browser) npx playwright test --headed # Run with debug UI npx playwright test --debug # Run specific test file with UI npx playwright test --ui tests/e2e/auth/login.spec.ts # Run with trace on failure npx playwright test --trace on # Run tests in parallel npx playwright test --workers=4 ``` ### Run Tests on Specific Browser ```bash # Chromium (default) npx playwright test --project=chromium # Note: Currently only Chromium is configured # Add more browsers in playwright.config.ts if needed ``` ### View Test Reports ```bash # Open HTML report npx playwright show-report # Report is generated in playwright-report/index.html ``` ## Test Structure ### Directory Layout ``` tests/e2e/ ├── helpers/ │ └── test-utils.ts # Shared test utilities ├── auth/ │ ├── login.spec.ts # Authentication flow tests │ ├── logout.spec.ts # Logout and session cleanup │ └── rbac.spec.ts # Role-based access control ├── landlord/ │ ├── dashboard.spec.ts # Landlord dashboard │ ├── properties.spec.ts # Property management │ ├── applications.spec.ts # Rental applications │ ├── leases.spec.ts # Lease management │ ├── maintenance.spec.ts # Maintenance requests │ └── payments.spec.ts # Financial data ├── tenant/ │ ├── applications.spec.ts # Tenant applications │ ├── leases.spec.ts # Tenant leases │ ├── maintenance.spec.ts # Maintenance requests │ └── payments.spec.ts # Payment management └── error-handling/ └── validation.spec.ts # Error scenarios and edge cases ``` ### Test File Structure ```typescript import { expect, test } from "../../helpers/test-utils.ts"; test.describe("Feature Name", () => { test.beforeEach(async ({ page }) => { // Set E2E mode for each test await page.addInitScript(() => { globalThis.localStorage.setItem("E2E_TEST", "true"); }); }); test("should do something specific", async ({ page }) => { // Navigate with E2E user header await page.goto("/some-route", { headers: { "x-e2e-user": "landlord", // or "tenant", "admin" }, }); // Wait for page to load await page.waitForTimeout(1000); // Perform actions and assertions await expect(page.locator("h1")).toContainText("Expected Text"); }); test.describe("Sub-feature", () => { test("should test sub-feature", async ({ page }) => { // Test implementation }); }); }); ``` ## Writing Tests ### Best Practices 1. **Use Descriptive Test Names** ```typescript test("should allow landlord to create property", async ({ page }) => { // Good: Clear and specific }); test("create property", async ({ page }) => { // Bad: Too vague }); ``` 2. **Track Console Errors** ```typescript const errors: string[] = []; page.on("console", (msg) => { if (msg.type() === "error") { errors.push(msg.text()); } }); // After test actions const filteredErrors = errors.filter( (err) => !err.includes("net::ERR_BLOCKED_BY_CLIENT"), ); expect(filteredErrors.length).toBe(0); ``` 3. **Use Conditional Assertions for Missing Features** ```typescript const element = page.locator("#some-feature"); if (await element.isVisible({ timeout: 3000 })) { await expect(element).toBeVisible(); // Additional assertions } ``` 4. **Group Related Tests with `test.describe`** ```typescript test.describe("Property Creation", () => { test("should show form", async ({ page }) => {}); test("should validate inputs", async ({ page }) => {}); test("should save property", async ({ page }) => {}); }); ``` 5. **Use E2E Test Fixtures** ```typescript // E2E mode is required to bypass OAuth await page.addInitScript(() => { globalThis.localStorage.setItem("E2E_TEST", "true"); }); // Use E2E user headers for authentication await page.goto("/protected-route", { headers: { "x-e2e-user": "landlord", }, }); ``` ### E2E User Fixtures The application provides E2E test fixtures for deterministic testing: - **`landlord`**: Alex Landlord (owns properties e2e-prop-1, e2e-prop-2) - **`tenant`**: Sam Tenant (has lease e2e-lease-1) - **`admin`**: Admin user (full system access) ### Common Test Patterns #### Navigation with Authentication ```typescript await page.goto("/landlord/dashboard", { headers: { "x-e2e-user": "landlord", }, }); await page.waitForTimeout(1000); // Allow for redirects/load ``` #### Form Interaction ```typescript // Fill form fields await page.locator('input[name="title"]').fill("Test Property"); await page.locator('textarea[name="description"]').fill("Description"); // Submit form await page.locator('button[type="submit"]').click(); // Wait for response await page.waitForTimeout(1000); ``` #### API Testing ```typescript const response = await page.request.get("/api/properties", { headers: { "x-e2e-user": "landlord", }, }); expect([200, 404].includes(response.status())).toBe(true); ``` #### Data Verification ```typescript // Verify page content const content = await page.content(); expect(content.includes("Expected Text")).toBe(true); // Or use locators await expect(page.locator("text=Expected Text")).toBeVisible(); ``` ## Debugging Tests ### Debug Mode Run tests with the Playwright Inspector: ```bash npx playwright test --debug ``` This opens: 1. A browser window 2. The Playwright Inspector for stepping through tests 3. Time-travel debugging capabilities ### UI Mode Run tests with the Playwright UI: ```bash npx playwright test --ui ``` Features: - Visual test explorer - Watch mode - Trace viewer - Time-travel debugging ### Headed Mode See the browser running the test: ```bash npx playwright test --headed ``` ### Slow Motion Run tests in slow motion to see what's happening: ```bash npx playwright test --headed --slow-mo=1000 ``` ### Screenshots on Failure Screenshots are automatically captured on failures. Find them in: ``` test-results/ ├── test-name/ │ ├── screenshot.png │ ├── trace.zip │ └── video.webm ``` ### Traces Traces capture everything that happened during the test: ```bash # Enable tracing for all tests npx playwright test --trace on # View a trace npx playwright show-trace test-results/trace.zip ``` ### Console Logging Add debugging statements: ```typescript test("should do something", async ({ page }) => { console.log("Current URL:", page.url()); const content = await page.content(); console.log("Page content preview:", content.substring(0, 200)); // Or use page.evaluate const result = await page.evaluate(() => { console.log("Browser console log"); return document.body.innerText; }); }); ``` ### Common Issues and Solutions #### Issue: "Element not found" **Solution**: Increase timeout or use conditional checks ```typescript // Instead of: await expect(page.locator("#element")).toBeVisible(); // Use: if (await page.locator("#element").isVisible({ timeout: 5000 })) { await expect(page.locator("#element")).toBeVisible(); } ``` #### Issue: "Test is too slow" **Solution**: Adjust timeouts in `playwright.config.ts` ```typescript export default defineConfig({ timeout: 60_000, // Overall test timeout expect: { timeout: 10_000, // Assertion timeout }, }); ``` #### Issue: "Tests fail in CI but pass locally" **Solutions**: 1. Check CI environment variables 2. Increase retries in `playwright.config.ts` 3. Use `fullyParallel: false` in CI 4. Check for timing issues ```typescript export default defineConfig({ retries: process.env.CI ? 2 : 0, fullyParallel: !process.env.CI, }); ``` #### Issue: "Database connection errors" **Solution**: Ensure database is running and accessible ```bash # Check database status psql -h localhost -U postgres -d cyberhybridhub -c "SELECT 1;" # Or use docker-compose docker-compose up -d postgres ``` ## CI/CD Integration ### GitHub Actions The project includes a GitHub Actions workflow (`.github/workflows/e2e-tests.yml`) that: 1. Runs E2E tests on every push and PR 2. Runs tests on a schedule (daily at 2 AM UTC) 3. Shards tests across 4 workers for parallel execution 4. Uploads test results, screenshots, videos, and traces 5. Publishes test results as PR comments ### Workflow Features - **Parallel Execution**: Tests run across 4 shards - **Retry on Failure**: Tests retry up to 2 times in CI - **Artifact Retention**: Results kept for 14 days - **Failure Artifacts**: Screenshots, videos, and traces kept for 7 days - **Test Summary**: JUnit XML results published to PR ### Manual Workflow Trigger Trigger tests manually from GitHub Actions: 1. Go to Actions tab in GitHub 2. Select "E2E Tests" workflow 3. Click "Run workflow" 4. Select branch and click "Run workflow" ### Local CI Testing Test CI workflow locally using [act](https://github.com/nektos/act): ```bash # Install act brew install act # macOS # or curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash # Run workflow act push --job e2e-tests ``` ## Troubleshooting ### Port Already in Use ```bash # Kill process using port 8080 lsof -ti:8080 | xargs kill -9 # Or use a different port PORT=8081 npx playwright test ``` ### Browser Installation Issues ```bash # Reinstall Playwright browsers npx playwright install --force --with-deps chromium # Or install all browsers npx playwright install --with-deps ``` ### Deno Installation Issues ```bash # Reinstall Deno rm -rf ~/.deno curl -fsSL https://deno.land/install.sh | sh ``` ### Cache Issues ```bash # Clear Deno cache deno cache --reload tests/e2e/**/*.spec.ts # Clear Node cache rm -rf node_modules package-lock.json npm install ``` ### Test Isolation Issues If tests interfere with each other: 1. **Use unique test data** (add timestamps) ```typescript const timestamp = Date.now(); await page.locator('input[name="title"]').fill(`Test ${timestamp}`); ``` 2. **Clean up after tests** ```typescript test.afterEach(async ({ page }) => { // Delete test data await page.request.delete("/api/test-data", { headers: { "x-e2e-user": "admin" }, }); }); ``` 3. **Disable parallel execution** ```typescript // In playwright.config.ts fullyParallel: false, workers: 1, ``` ### Database Issues ```bash # Reset test database psql -h localhost -U postgres -c "DROP DATABASE IF EXISTS cyberhybridhub_test;" psql -h localhost -U postgres -c "CREATE DATABASE cyberhybridhub_test;" # Run migrations deno task db:push ``` ## Performance Tips 1. **Use parallel execution** for faster test runs 2. **Reuse browser context** when possible 3. **Avoid unnecessary waits** - use `waitForSelector` instead of `waitForTimeout` 4. **Group tests** by feature to reduce setup overhead 5. **Use API tests** for data validation (faster than UI) ## Additional Resources - [Playwright Documentation](https://playwright.dev) - [Playwright Best Practices](https://playwright.dev/docs/best-practices) - [Project CLAUDE.md](../CLAUDE.md) - Project-specific guidelines - [Test Examples](./e2e/) - Reference implementations ## Support For issues or questions: 1. Check this guide's troubleshooting section 2. Review Playwright documentation 3. Check existing test files for examples 4. Consult project documentation in `CLAUDE.md`