integratedhub/tests/e2e/README.md
2026-02-14 02:20:19 +00:00

324 lines
8.2 KiB
Markdown

# E2E Test Helpers & Documentation
This directory contains end-to-end tests using Playwright with deterministic
test fixtures.
## Overview
The E2E test infrastructure provides:
- **Deterministic Testing**: Consistent time, random values, and no animations
- **E2E User Authentication**: Bypass normal auth via `x-e2e-user` header
- **Fixture Data**: Pre-populated test data in `services/e2eFixtures.ts`
- **Reusable Helpers**: Common test utilities in `helpers/fixtures.ts`
## Available E2E Users
The following test users are available (defined in `services/e2eFixtures.ts`):
| User Type | User ID | Name | Description |
| --------------------- | -------------------- | --------------- | --------------------------------------------------- |
| `landlord` | e2e-landlord-1 | Alex Landlord | Fully onboarded landlord with properties and leases |
| `landlord-2` | e2e-landlord-2 | Jordan Landlord | Second landlord for multi-user testing |
| `landlord-onboarding` | e2e-landlord-setup-1 | (empty) | New landlord not yet onboarded |
| `tenant` | e2e-tenant-1 | Taylor Tenant | Tenant with active lease |
| `admin` | e2e-admin-1 | Admin User | Admin with full system access |
## Test Data Available
Each E2E user has pre-populated data:
### Landlord (Alex)
- **Properties**: Skyline Lofts (available), Maple Row (occupied)
- **Leases**: 1 active lease for Maple Row
- **Messages**: 11 messages (mix of read/unread)
- **Maintenance Requests**: 2 requests (open, in-progress)
- **Payment Transactions**: 3 completed payments
### Tenant (Taylor)
- **Leases**: 1 active lease at Maple Row
- **Messages**: Conversations with landlord
- **Maintenance Requests**: 2 submitted requests
- **Payment Methods**: 1 default payment method
- **Payment Transactions**: Payment history
### Landlord-2 (Jordan)
- **Properties**: Riverside Condo
- **Leases**: None (testing landlord with no leases)
## How to Write Tests
### Basic UI Test
```typescript
import { expect, test } from "@playwright/test";
import {
applyDeterministicPage,
ROUTES,
useE2EUser,
} from "../helpers/fixtures.ts";
test("landlord views dashboard", async ({ page }) => {
// 1. Apply deterministic behavior
await applyDeterministicPage(page);
// 2. Set E2E user context
await useE2EUser(page, "landlord");
// 3. Navigate and test
await page.goto(ROUTES.landlordDashboard);
await expect(page.locator("h1")).toContainText("Dashboard");
});
```
### API Test
```typescript
import { expect, test } from "@playwright/test";
import { e2eHeaders } from "../helpers/fixtures.ts";
test("API returns landlord properties", async ({ request }) => {
const response = await request.get("/api/retrieve/properties", {
headers: e2eHeaders("landlord"),
});
expect(response.ok()).toBeTruthy();
const data = await response.json();
expect(data.properties).toHaveLength(2);
});
```
### Test with Generated Data
```typescript
import { generateTestProperty } from "../helpers/fixtures.ts";
test("create new property", async ({ page, request }) => {
const newProperty = generateTestProperty({
title: "My Test Property",
bedrooms: 3,
});
const response = await request.post("/api/create/property", {
headers: e2eHeaders("landlord"),
data: newProperty,
});
expect(response.ok()).toBeTruthy();
});
```
## Helper Functions Reference
### `useE2EUser(page, userType)`
Sets the E2E user context for browser tests.
- **Parameters**:
- `page`: Playwright Page object
- `userType`: E2E user type ("landlord", "tenant", etc.)
- **What it does**:
- Injects `x-e2e-user` header in all requests
- Sets `localStorage` flag to skip encryption
### `applyDeterministicPage(page)`
Makes page behavior deterministic for consistent tests.
- **Parameters**:
- `page`: Playwright Page object
- **What it does**:
- Stubs `Date.now()` to return fixed base time
- Stubs `Math.random()` for consistent sequences
- Disables CSS animations/transitions
- Sets timezone to UTC
### `e2eHeaders(userType)`
Creates headers for API requests with E2E authentication.
- **Parameters**:
- `userType`: E2E user type
- **Returns**: Headers object with `x-e2e-user` and `Content-Type`
### `generateTestProperty(overrides?)`
Generates a unique test property object.
- **Parameters**:
- `overrides`: Optional partial property data
- **Returns**: Complete property object with unique values
### `generateTestLease(overrides?)`
Generates a unique test lease object.
### `generateTestMessage(overrides?)`
Generates a unique test message object.
### `generateTestMaintenanceRequest(overrides?)`
Generates a unique test maintenance request object.
### `expectVisible(page, selector)`
Asserts an element is visible.
### `expectText(page, selector, text)`
Asserts an element contains specific text.
### `expectNoConsoleErrors(page)`
Asserts no console errors occurred.
### `waitForPageLoad(page)`
Waits for page to fully load (networkidle + domcontentloaded).
## Routes Reference
Common routes are available in `ROUTES` constant:
```typescript
ROUTES.home; // "/"
ROUTES.landlordDashboard; // "/landlord/dashboard"
ROUTES.tenantDashboard; // "/tenant/dashboard"
ROUTES.landlordProperties; // "/landlord/properties"
ROUTES.landlordLeases; // "/landlord/leases"
ROUTES.tenantLeases; // "/tenant/leases"
// ... and more
```
## Running Tests
### Run all E2E tests
```bash
deno task e2e
```
### Run specific test file
```bash
deno run -A npm:playwright test tests/e2e/example.test.ts
```
### Run with UI mode
```bash
deno task e2e:ui
```
### View test report
```bash
deno task e2e:report
```
## Best Practices
### 1. Always Apply Deterministic Behavior
```typescript
test.beforeEach(async ({ page }) => {
await applyDeterministicPage(page);
});
```
### 2. Set User Context Early
```typescript
test("my test", async ({ page }) => {
await useE2EUser(page, "landlord"); // Do this first
await page.goto("/some/page"); // Then navigate
});
```
### 3. Use Generated Data for Uniqueness
```typescript
// Good - unique every time
const property = generateTestProperty();
// Bad - may conflict
const property = { title: "Test Property" };
```
### 4. Wait for Page Load
```typescript
await page.goto("/some/page");
await waitForPageLoad(page); // Always wait after navigation
```
### 5. Check for Console Errors
```typescript
await expectNoConsoleErrors(page); // Catch JS errors
```
### 6. Use Resilient Assertions
```typescript
// Good - checks for section header
await expectText(page, "h1", "Dashboard");
// Bad - assumes specific data that may change
await expectText(page, "text=Skyline Lofts", "Skyline Lofts");
```
## Data Isolation Testing
The E2E infrastructure supports testing data isolation:
```typescript
test("users cannot see each other's data", async ({ page }) => {
// Landlord view
await useE2EUser(page, "landlord");
await page.goto(ROUTES.landlordProperties);
await expect(page.locator("text=Skyline Lofts")).toBeVisible();
// Tenant view (should not see landlord properties)
await useE2EUser(page, "tenant");
await page.goto(ROUTES.tenantLeases);
await expect(page.locator("text=Skyline Lofts")).not.toBeVisible();
});
```
## Troubleshooting
### Tests Failing with "Not Found"
- Ensure the dev server is running with `E2E_TEST=true`
- Check that routes exist in the codebase
### Tests Flaky / Inconsistent
- Ensure `applyDeterministicPage()` is called
- Check that you're using `waitForPageLoad()` after navigation
- Verify no real-time timers or intervals in your code
### Data Not Persisting
- Remember: E2E tests use in-memory fixtures
- Data created in one test persists for that test run
- Use unique generated data to avoid conflicts
### Authentication Issues
- Verify `x-e2e-user` header is being set
- Check `services/stateMiddleware.ts` for E2E logic
- Ensure `E2E_TEST=true` is set in environment
## Additional Resources
- [Playwright Documentation](https://playwright.dev/)
- `services/e2eFixtures.ts` - Fixture data definitions
- `services/stateMiddleware.ts` - E2E authentication bypass
- `playwright.config.ts` - Test configuration