This directory contains end-to-end tests for the TimeClout application using Playwright.
The e2e tests have been significantly improved to reduce flakiness and improve reliability. Key improvements include:
- Page Object Model: Centralized selectors and common test patterns
- Retry Logic: Exponential backoff for flaky operations
- Better Waiting Strategies: Replaced
waitForTimeoutwith proper element waiting - Accessibility-First Selectors: Using ARIA labels and semantic selectors
- Improved Error Handling: Better error messages and recovery mechanisms
tests/e2e/
├── auth/ # Authentication tests
├── fixtures/ # Test fixtures and utilities
├── utils/ # Test utilities and page objects
├── config/ # Test configuration
├── global-setup.ts # Global test setup
├── global-teardown.ts # Global test cleanup
└── company-creation-workflow.spec.ts # Main workflow test
The Page Objects provide reliable selectors and common test patterns:
const pageObjects = createPageObjects(page);
// Wait for forms to load
await pageObjects.waitForCompanyForm();
await pageObjects.waitForUnitForm();
await pageObjects.waitForTeamForm();
// Create entities
await pageObjects.createCompany("Company Name");
await pageObjects.createUnit("Unit Name");
await pageObjects.createTeam("Team Name");
// Navigate and wait
await pageObjects.waitForNavigation(/\/companies\/.*/);Enhanced test utilities with retry logic and better waiting strategies:
const testHelpers = createTestHelpers(userManagement);
// Wait for conditions with retry
await testHelpers.waitForCondition(async () => {
return await someCondition();
}, 30000);
// Retry operations with exponential backoff
await testHelpers.retryWithBackoff(
async () => {
return await flakyOperation();
},
3,
1000
);Improved user management with better waiting strategies:
const userManagement = createUserManagement(page);
// Create and login user
const user = await userManagement.createAndLoginUser("Test User");
// Cleanup
await userManagement.cleanupUser(user);- Install dependencies:
pnpm install - Install Playwright browsers:
pnpm test:e2e:install - Set up environment variables (see
.env.example)
# Run all e2e tests
pnpm test:e2e
# Run tests with UI
pnpm test:e2e:ui
# Run tests in headed mode
pnpm test:e2e:headed
# Run specific test file
pnpm test:e2e company-creation-workflow.spec.ts
# Debug tests
pnpm test:e2e:debug- Retries: 2 retries in CI, 1 in development
- Timeouts: 3 minutes global timeout, 30 seconds navigation timeout
- Browser: Chromium with reliability-focused launch arguments
- Workers: Reduced to 1 in CI to avoid resource conflicts
- Testmail: Email testing service configuration
- Timeouts: Configurable timeouts for different operations
- Recording: Screenshots, videos, and traces on failure
Instead of direct selectors, use the page objects:
// ❌ Bad - direct selector
await page.locator(".company-name-input").fill("Name");
// ✅ Good - page object
await pageObjects.createCompany("Name");Use proper waiting strategies instead of timeouts:
// ❌ Bad - flaky timeout
await page.waitForTimeout(2000);
// ✅ Good - wait for element state
await element.waitFor({ state: "visible" });
await page.waitForLoadState("networkidle");Prefer accessibility-focused selectors:
// ❌ Bad - generic class selector
await page.locator(".button").click();
// ✅ Good - accessibility selector
await page.locator('button[aria-label="Create company"]').click();Use retry logic for flaky operations:
await testHelpers.retryWithBackoff(
async () => {
return await flakyOperation();
},
3,
1000
);Always verify that operations completed successfully:
// Fill form field
await pageObjects.fillFormField(".input", "value");
// Verify the value was set
const actualValue = await pageObjects.input.inputValue();
expect(actualValue).toBe("value");- Element Not Found: Check if the selector matches the actual component structure
- Timeout Errors: Increase timeouts or check for network issues
- Flaky Tests: Use retry logic and better waiting strategies
Run tests in debug mode to step through execution:
pnpm test:e2e:debugView detailed test execution traces:
# Open trace file in browser
npx playwright show-trace test-results/trace.zipimport { testWithUserManagement } from "../fixtures/test-fixtures";
testWithUserManagement.describe("New Feature", () => {
testWithUserManagement(
"should work correctly",
async ({ page, pageObjects }) => {
// Test implementation
}
);
});Extend the PageObjects class with new selectors:
// In utils/page-objects.ts
get newFeatureButton() {
return this.page.locator('button[aria-label="New Feature"]');
}Extend TestHelpers with new utility methods:
// In utils/test-helpers.ts
async newFeatureHelper(): Promise<void> {
// Helper implementation
}- Parallel Execution: Tests run in parallel by default
- Resource Management: Reduced workers in CI to avoid conflicts
- Cleanup: Proper cleanup after each test to prevent resource leaks
- Timeouts: Reasonable timeouts to prevent hanging tests
The tests are configured for CI/CD environments:
- Retries: Automatic retry of failed tests
- Artifacts: Screenshots, videos, and traces on failure
- Reporting: Multiple report formats (JSON, JUnit, HTML)
- Resource Optimization: Reduced workers and optimized browser settings