This directory contains end-to-end (e2e) tests for the Zuko web application using Playwright and Nx.
The test suite follows Nx best practices and implements the Page Object Model pattern for maintainable and scalable tests.
apps/web-e2e/
├── src/
│ ├── fixtures/ # Test fixtures and utilities
│ │ ├── test-fixtures.ts # Custom test fixtures with page objects
│ │ ├── test-data.ts # Test data and generators
│ │ └── index.ts
│ ├── pages/ # Page Object Models
│ │ ├── BasePage.ts # Base page with common methods
│ │ ├── SignInPage.ts # Sign-in page
│ │ ├── ContactsPage.ts # Contacts page
│ │ ├── SettingsPage.ts # Settings page
│ │ ├── ChatPage.ts # Chat page
│ │ └── index.ts
│ ├── example.spec.ts # Homepage/navigation tests
│ ├── auth.spec.ts # Authentication tests
│ ├── contacts.spec.ts # Contacts feature tests
│ └── settings.spec.ts # Settings feature tests
├── playwright.config.ts # Playwright configuration
└── README.md
## Running Tests
### Run E2E tests
```bash
NODE_ENV=test bunx nx run web-e2e:e2e
NODE_ENV=test bunx nx e2e web-e2e --uiNODE_ENV=test bunx nx run web-e2e:e2e -- src/auth.spec.ts# Chromium (default)
NODE_ENV=test bunx nx e2e web-e2e -- --project=chromium
# Firefox
NODE_ENV=test bunx nx e2e web-e2e -- --project=firefox
# WebKit (Safari)
NODE_ENV=test bunx nx e2e web-e2e -- --project=webkitNODE_ENV=test bunx nx e2e web-e2e -- --headedNODE_ENV=test bunx nx e2e web-e2e -- --debugNODE_ENV=test bunx nx e2e-ci web-e2eAfter running tests, reports are generated in:
- HTML Report:
apps/web-e2e/test-output/playwright/report/index.html - JSON Report:
apps/web-e2e/test-output/playwright/results.json - JUnit Report:
apps/web-e2e/test-output/playwright/results.xml
Open HTML report:
npx playwright show-report apps/web-e2e/test-output/playwright/reportTests use the Page Object Model (POM) pattern for better maintainability:
import { test, expect } from './fixtures';
test('example test with page objects', async ({ signInPage }) => {
await signInPage.goto();
await signInPage.verifyPageLoaded();
await signInPage.clickGitHubSignIn();
});BasePage- Common functionality for all pagesSignInPage- Sign-in page interactionsContactsPage- Contacts list and managementSettingsPage- Settings and configurationChatPage- Chat interface
Custom fixtures provide pre-configured page objects:
import { test, expect } from './fixtures';
test('my test', async ({ signInPage, contactsPage, page }) => {
// Use page objects directly
await signInPage.goto();
await contactsPage.goto();
});Test data helpers are available in fixtures/test-data.ts:
import { TEST_CONTACTS, generateUniqueTestData } from './fixtures';
// Use predefined test data
const contact = TEST_CONTACTS.validContact;
// Generate unique test data with timestamp
const uniqueContact = generateUniqueTestData(TEST_CONTACTS.validContact);BASE_URL- Base URL for the application (default:http://localhost:3000)CI- Set totruefor CI environment (enables retries, serial execution)
Key settings in playwright.config.ts:
- Browsers: Chromium, Firefox, WebKit
- Timeout: 60s per test
- Retries: 2 retries on CI, 0 locally
- Screenshots: On failure only
- Video: Retained on failure
- Traces: On first retry
- Web Server: Automatically starts dev server before tests
The test suite includes authentication setup using Playwright's storageState feature. Authentication is handled automatically via global setup.
- Global Setup (
src/global-setup.ts): Runs once before all tests to authenticate - Auth State Storage: Saves authentication cookies/session to
.auth/user.json - Authenticated Tests: Use
authenticatedTestfixture to load the saved state
You have two options for authentication:
Create a test API endpoint that logs in a test user:
// In your backend (e.g., /api/auth/test-login)
// This endpoint should only be available in test environments
export async function testLogin(email, password) {
// Authenticate and return session cookie
}Then update src/global-setup.ts:
async function authenticateViaAPI(page: any, baseURL: string) {
const response = await page.request.post(`${baseURL}/api/auth/test-login`, {
data: {
email: process.env.TEST_USER_EMAIL || 'test@example.com',
password: process.env.TEST_USER_PASSWORD || 'test-password',
},
});
await page.goto(baseURL);
}For GitHub OAuth, you need test credentials:
# Set environment variables
export GITHUB_TEST_USERNAME="your-test-account"
export GITHUB_TEST_PASSWORD="your-test-password"The global setup will handle the OAuth flow automatically.
Use the authenticatedTest fixture for tests that require authentication:
import { test, expect, authenticatedTest } from './fixtures';
// Regular test (no auth)
test('public page test', async ({ page }) => {
await page.goto('/');
});
// Authenticated test (uses stored auth state)
authenticatedTest('authenticated feature', async ({ page, contactsPage }) => {
await contactsPage.goto();
// Now authenticated - can access protected pages
});Set these environment variables for authentication:
# For API authentication
TEST_USER_EMAIL=test@example.com
TEST_USER_PASSWORD=test-password
# For OAuth authentication
GITHUB_TEST_USERNAME=your-test-account
GITHUB_TEST_PASSWORD=your-test-passwordIf authentication fails:
- Check auth state file exists:
apps/web-e2e/.auth/user.json - Run setup manually:
npx playwright test --project=setup - Check environment variables are set correctly
- Verify test endpoint is accessible and working
- Check logs during global setup for errors
The global setup will warn you if authentication fails, but tests will continue (and may skip if auth is required).
- Use Page Objects - Encapsulate page interactions in page objects
- Use Fixtures - Leverage custom fixtures for common setup
- Descriptive Test Names - Use clear, descriptive test names
- Wait for Elements - Always wait for elements before interacting
- Avoid Hard Waits - Use
waitForinstead ofwaitForTimeout - Test Independence - Tests should be independent and not rely on order
- Clean Up - Clean up test data after tests (when applicable)
For CI/CD pipelines:
# Run all e2e tests with CI optimizations
bunx nx e2e-ci web-e2e
# Run with specific configuration
CI=true bunx nx e2e web-e2eCI mode enables:
- 2 retries per test
- Serial execution (1 worker)
- Strict mode (no test.only)
- Full reports
- Run with
--workers=Nto use multiple workers - Use
--project=chromiumto run on single browser - Check if dev server is slow to start
- Increase timeouts in
playwright.config.ts - Add proper wait conditions
- Check for race conditions
- Use Playwright Inspector:
bunx nx e2e web-e2e -- --debug - Check selectors in page objects
- Verify element is visible before interaction
- Implement proper authentication setup
- Use
storageStatefor persistent sessions - Mock OAuth flow in tests
When adding new tests:
- Create page objects in
src/pages/ - Add test data to
src/fixtures/test-data.ts - Write tests following existing patterns
- Update this README if needed
- Ensure tests pass locally before committing