This project follows a Test Pyramid Architecture designed for optimal performance, reliability, and maintainability. This guide explains when to use each testing approach and provides examples for both human developers and AI agents.
πΊ Test Pyramid (Performance Optimized)
βββ E2E Tests (Cypress) - 16 tests, ~60 seconds
β βββ Complete user workflows
β βββ Cross-page navigation with data persistence
β βββ File import/export operations
β βββ Service worker UI interactions
βββ Integration Tests (Jest) - Component interaction testing
β βββ Component composition and prop passing
β βββ Modal focus management and keyboard navigation
β βββ Accessibility features and ARIA compliance
β βββ Page-level component integration
βββ Unit Tests (Jest) - 135+ tests, ~15 seconds
βββ Component logic and state management
βββ Hook behavior and edge cases
βββ Utility functions and algorithms
βββ State machine transitions
Component Logic & Behavior
- Form validation and error handling
- Input validation and sanitization
- Component state management
- Prop handling and default values
- Event handler logic
User Interface Elements
- Button states and interactions
- Modal lifecycle (open/close/focus)
- Theme switching behavior
- Loading states and transitions
- Error boundary behavior
Accessibility Features
- ARIA labels and semantic HTML
- Keyboard navigation patterns
- Screen reader compatibility
- Focus management
- Color contrast compliance
Business Logic & Utilities
- Activity state machine transitions
- Time calculations and formatting
- Data transformation functions
- Validation algorithms
- Storage operations (localStorage/sessionStorage)
React Hooks & State
- Custom hook behavior
- State updates and side effects
- Context provider functionality
- Effect cleanup and dependencies
- Memoization and performance optimizations
Complete User Workflows
- Full CRUD operations (Create β Read β Update β Delete)
- Multi-step user journeys
- Data persistence across actions
- Complex interaction sequences
Cross-Page Navigation
- Route transitions with state preservation
- Browser history management
- Deep linking functionality
- URL parameter handling
File Operations
- File upload/download workflows
- Import/export functionality
- Drag and drop interactions
- File validation and processing
Browser-Specific Features
- Service worker update notifications
- Offline/online state transitions
- PWA installation prompts
- Push notifications (if implemented)
Integration Scenarios
- Multiple components working together
- Real browser environment behavior
- Network request/response cycles
- Authentication flows (if implemented)
// β
GOOD: Test component behavior in Jest
describe('ActivityForm Validation', () => {
it('should show error for empty activity name', () => {
render(<ActivityForm />);
fireEvent.click(screen.getByRole('button', { name: /save/i }));
expect(screen.getByRole('alert')).toHaveTextContent('Activity name is required');
expect(screen.getByLabelText(/activity name/i)).toHaveAttribute('aria-invalid', 'true');
});
it('should handle special characters in activity names', () => {
render(<ActivityForm onSubmit={mockSubmit} />);
const input = screen.getByLabelText(/activity name/i);
fireEvent.change(input, { target: { value: 'Test @#$%^&*()_+ Activity' } });
fireEvent.click(screen.getByRole('button', { name: /save/i }));
expect(mockSubmit).toHaveBeenCalledWith({
name: 'Test @#$%^&*()_+ Activity'
});
});
});// β
GOOD: Test complete user workflow in Cypress
describe('Activity Management Workflow', () => {
it('should complete full CRUD lifecycle', () => {
cy.visit('/activities');
// Create
cy.contains('Add Activity').click();
cy.get('[role="dialog"]').within(() => {
cy.get('input[type="text"]').type('Test Activity');
});
cy.get('button').contains('Save').click();
cy.contains('Test Activity').should('be.visible');
// Read
cy.contains('Test Activity').should('be.visible');
// Update
cy.get('button').contains('Edit').first().click();
cy.get('[role="dialog"]').within(() => {
cy.get('input[type="text"]').clear().type('Updated Activity');
});
cy.get('button').contains('Save').click();
cy.contains('Updated Activity').should('be.visible');
// Delete
cy.get('button').contains('Delete').first().click();
cy.get('[role="dialog"]').within(() => {
cy.get('button').contains('Delete').click();
});
cy.contains('Updated Activity').should('not.exist');
});
});Component Logic Testing
// β BAD: Testing component logic in Cypress
it('should validate form fields', () => {
cy.visit('/activities');
cy.contains('Add Activity').click();
cy.get('button').contains('Save').click();
// This is testing component logic, should be Jest
});Simple Interactions
// β BAD: Testing simple button clicks in Cypress
it('should toggle theme', () => {
cy.visit('/');
cy.get('[data-testid="theme-toggle"]').click();
// This is testing component behavior, should be Jest
});Cross-Page Workflows
// β BAD: Testing navigation in Jest
it('should navigate to activities page', () => {
// Jest can't test real navigation between pages
// This needs Cypress for proper browser environment
});File Upload Workflows
// β BAD: Testing file uploads in Jest
it('should upload activity file', () => {
// File upload needs real browser environment
// This should be tested in Cypress
});- Jest Tests: ~15 seconds for 135+ tests
- Cypress Tests: ~60 seconds for 16 tests
- Performance Ratio: Jest is 15x faster per test
-
Favor Jest When Possible
- 85% of tests should be Jest
- 15% of tests should be Cypress
- Only use Cypress for unique value
-
Mock External Dependencies
- Mock API calls in Jest tests
- Mock browser APIs when not essential
- Keep tests isolated and fast
-
Parallel Execution
- Jest runs tests in parallel by default
- Cypress tests run sequentially
- Structure tests for optimal parallelization
src/
βββ components/
β βββ ActivityForm.tsx
β βββ __tests__/
β βββ ActivityForm.test.tsx # Component logic
β βββ ActivityForm.integration.test.tsx # Component interaction
β βββ ActivityForm.accessibility.test.tsx # A11y features
βββ hooks/
β βββ useActivityState.ts
β βββ __tests__/
β βββ useActivityState.test.tsx # Hook behavior
βββ utils/
βββ activityStateMachine.ts
βββ __tests__/
βββ activityStateMachine.test.tsx # Business logic
cypress/e2e/
βββ activity-crud.cy.ts # Complete workflows
βββ service-worker.cy.ts # UI integration
- Jest:
*.test.tsxfor unit tests,*.integration.test.tsxfor integration - Cypress:
*.cy.tsfor end-to-end workflows - Test IDs: Use
data-testidattributes for reliable selectors
# Jest tests (fast, frequent)
npm test # All Jest tests
npm test -- --watch # Watch mode
npm test -- --testPathPatterns="Form" # Specific patterns
# Cypress tests (slow, essential)
npm run cypress:run # Headless e2e tests
npm run cypress # Interactive test runner# Fast feedback pipeline
npm run test && npm run lint && npm run type-check
# Complete validation pipeline
npm run test && npm run cypress:run && npm run build- Start with Jest - Write unit tests first
- Add Cypress sparingly - Only for unique user workflows
- Think performance - Fast tests enable better TDD
- Mock dependencies - Keep tests isolated and reliable
- Default to Jest - Unless explicitly asked for e2e testing
- Check existing coverage - Avoid duplicating test scenarios
- Follow decision matrix - Use the guidelines above
- Consider performance - Prefer faster test execution
- Test-driven development - Write tests before implementation
- Descriptive test names - Clear intention and expected behavior
- Arrange-Act-Assert - Consistent test structure
- Single responsibility - One concept per test
This architecture ensures optimal performance while maintaining comprehensive coverage and enabling confident development workflows.