Testing Guide
Testing Guide
Section titled “Testing Guide”Version: 1.0.13
Status: Active
Overview
Section titled “Overview”This guide covers testing strategies, patterns, and requirements for the Puppeteer MCP project. We maintain comprehensive test coverage across unit, integration, and end-to-end tests.
Test Infrastructure
Section titled “Test Infrastructure”Technology Stack
Section titled “Technology Stack”- Test Runner: Jest with ESM support
- Assertion Library: Built-in Jest matchers
- Mocking: Jest mocks + custom Puppeteer mocks
- Coverage: Jest coverage with tiered thresholds
Test Organization
Section titled “Test Organization”tests/├── unit/ # Unit tests for individual components├── integration/ # Integration tests for subsystems├── e2e/ # End-to-end workflow tests├── benchmark/ # Performance benchmarks└── __mocks__/ # Shared mock implementations
Testing Standards
Section titled “Testing Standards”Coverage Requirements
Section titled “Coverage Requirements”Following TS:JEST standards from William Zujkowski’s Standards:
Component | Coverage Target | Current |
---|---|---|
Global | 15-18% | ✅ |
Auth/Security | 80-90% | ✅ |
Utilities | 50-80% | ✅ |
Browser Actions | 70%+ | ✅ |
Test Categories
Section titled “Test Categories”Unit Tests
Section titled “Unit Tests”Test individual functions and classes in isolation:
describe('BrowserPool', () => { it('should acquire browser instance', async () => { const pool = new BrowserPool({ maxSize: 2 }); const browser = await pool.acquire();
expect(browser).toBeDefined(); expect(pool.getActiveCount()).toBe(1); });});
Integration Tests
Section titled “Integration Tests”Test component interactions:
describe('Session with Context Integration', () => { it('should create context with valid session', async () => { const sessionId = await sessionStore.create(userId); const contextId = await contextStore.create(sessionId, { name: 'test-context', });
expect(contextId).toMatch(/^ctx-/); });});
E2E Tests
Section titled “E2E Tests”Test complete workflows:
describe('Browser Automation Workflow', () => { it('should complete form submission', async () => { // Create context const context = await api.post('/contexts', { name: 'form-test', });
// Navigate and interact await api.post(`/contexts/${context.id}/execute`, { action: 'navigate', params: { url: 'https://example.com/form' }, });
// Verify results const screenshot = await api.post(`/contexts/${context.id}/execute`, { action: 'screenshot', });
expect(screenshot.data).toBeDefined(); });});
Writing Tests
Section titled “Writing Tests”Test Structure
Section titled “Test Structure”Follow the AAA pattern:
describe('FeatureName', () => { // Arrange - Setup beforeEach(() => { // Setup test environment });
it('should perform expected behavior', async () => { // Arrange const input = createTestInput();
// Act const result = await performAction(input);
// Assert expect(result).toMatchExpectedOutput(); });
// Cleanup afterEach(() => { // Restore state });});
Mocking Strategies
Section titled “Mocking Strategies”Puppeteer Mocking
Section titled “Puppeteer Mocking”Use our custom Puppeteer mock for unit tests:
import { createMockBrowser } from '../__mocks__/puppeteer';
const mockBrowser = createMockBrowser({ pages: [ { url: 'https://example.com', title: 'Example Page', }, ],});
External Services
Section titled “External Services”Mock external dependencies:
jest.mock('../../src/store/session-store');const mockSessionStore = sessionStore as jest.Mocked<typeof sessionStore>;
mockSessionStore.get.mockResolvedValue({ id: 'session-123', userId: 'user-456',});
Testing Browser Actions
Section titled “Testing Browser Actions”Each browser action should have comprehensive tests:
describe('ClickAction', () => { it('should click element by selector', async () => { const page = createMockPage(); const action = new ClickAction();
await action.execute(page, { selector: '#submit-button', });
expect(page.click).toHaveBeenCalledWith('#submit-button'); });
it('should handle missing elements gracefully', async () => { const page = createMockPage(); page.click.mockRejectedValue(new Error('Element not found'));
await expect( action.execute(page, { selector: '#missing', }), ).rejects.toThrow('Element not found'); });});
Running Tests
Section titled “Running Tests”Commands
Section titled “Commands”# Run all testsnpm test
# Run specific test suitenpm test -- auth.test.ts
# Run with coveragenpm run test:coverage
# Watch mode for developmentnpm run test:watch
# Run only unit testsnpm test -- tests/unit
# Run only integration testsnpm run test:integration
# Run benchmarksnpm run test:benchmark
Debug Mode
Section titled “Debug Mode”# Debug with Node inspectornode --inspect-brk node_modules/.bin/jest --runInBand
# Debug specific testnpm test -- --testNamePattern="should authenticate" --verbose
Test Patterns
Section titled “Test Patterns”Async Testing
Section titled “Async Testing”Always properly handle async operations:
// Good - Proper async handlingit('should handle async operation', async () => { const result = await asyncOperation(); expect(result).toBeDefined();});
// Bad - Missing awaitit('should handle async operation', () => { const result = asyncOperation(); // Missing await! expect(result).toBeDefined(); // Tests Promise, not result});
Error Testing
Section titled “Error Testing”Test both success and failure paths:
describe('Error Handling', () => { it('should handle network errors', async () => { mockFetch.mockRejectedValue(new Error('Network error'));
await expect(apiCall()).rejects.toThrow('Network error'); });
it('should handle validation errors', async () => { const result = await validateInput({ invalid: true });
expect(result.error).toBeDefined(); expect(result.error.code).toBe('VALIDATION_ERROR'); });});
Performance Testing
Section titled “Performance Testing”Include performance benchmarks:
describe('Performance', () => { it('should process requests within SLA', async () => { const start = performance.now();
await processRequest(testData);
const duration = performance.now() - start; expect(duration).toBeLessThan(100); // 100ms SLA });});
Best Practices
Section titled “Best Practices”1. Test Isolation
Section titled “1. Test Isolation”Each test should be independent:
beforeEach(() => { jest.clearAllMocks(); // Reset test state});
2. Descriptive Names
Section titled “2. Descriptive Names”Use clear, behavior-focused names:
// Goodit('should return 401 when authentication token is expired');
// Badit('test auth');
3. Avoid Test Logic
Section titled “3. Avoid Test Logic”Keep tests simple and declarative:
// Goodexpect(result).toEqual(expectedOutput);
// Badif (condition) { expect(result).toBe(value1);} else { expect(result).toBe(value2);}
4. Use Test Builders
Section titled “4. Use Test Builders”Create reusable test data builders:
const createTestUser = (overrides = {}) => ({ id: 'user-123', email: 'test@example.com', role: 'user', ...overrides,});
Continuous Integration
Section titled “Continuous Integration”Tests run automatically on:
- Every push via Git hooks
- Pull requests via GitHub Actions
- Scheduled nightly runs
CI Requirements
Section titled “CI Requirements”- All tests must pass
- Coverage thresholds must be met
- No ESLint errors in test files
- Performance benchmarks within limits
Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”Timeout Errors
Section titled “Timeout Errors”// Increase timeout for slow operationsit('should handle large file', async () => { await processLargeFile();}, 30000); // 30 second timeout
Memory Leaks
Section titled “Memory Leaks”// Ensure cleanupafterEach(async () => { await browserPool.closeAll(); global.gc?.(); // Force garbage collection if available});
Flaky Tests
Section titled “Flaky Tests”- Add retries for network-dependent tests
- Use stable test data
- Avoid time-dependent assertions
- Mock external dependencies