Global WatchGlobal Watch Docs
Testing

Testing Overview

Testing Overview

Global Watch employs a comprehensive testing strategy that combines multiple testing approaches to ensure code quality, reliability, and maintainability. Our testing philosophy emphasizes high confidence with minimal manual intervention.

Testing Philosophy

Our testing approach follows these core principles:

  1. Test-Driven Development (TDD) - Write tests first, then implement
  2. Dual Testing Approach - Combine unit tests and property-based tests
  3. Automation First - Minimize manual testing through comprehensive automation
  4. High Coverage - Maintain minimum 70% coverage across the codebase

Testing Stack

Global Watch uses industry-standard testing tools:

ToolPurposeLocation
VitestUnit and integration testsapps/web/**/*.test.ts
fast-checkProperty-based testingapps/web/**/*.test.ts
PlaywrightEnd-to-end testingapps/e2e/tests/

Test Types

Unit Tests

Unit tests verify individual functions, classes, and modules work correctly in isolation:

  • Test specific examples and edge cases
  • Fast execution (milliseconds)
  • High coverage of business logic
  • Co-located with source files using .test.ts suffix

Learn more about Unit Testing →

Property-Based Tests

Property-based tests verify universal properties hold across all valid inputs:

  • Generate random test cases automatically
  • Find edge cases humans might miss
  • Validate invariants and contracts
  • Minimum 100 iterations per property

Learn more about Property-Based Testing →

End-to-End Tests

E2E tests verify complete user flows work correctly:

  • Test real browser interactions
  • Validate multi-step workflows
  • Cross-browser compatibility
  • Integration with real services

Learn more about E2E Testing →

Coverage Requirements

Global Watch maintains strict coverage requirements:

CategoryMinimum Coverage
Overall70%
Core components90%
Business logic80%
Domain entities90%
Server actions80%

Running Tests

Quick Commands

# Run all unit tests
pnpm --filter web test:run

# Run tests in watch mode
pnpm --filter web test

# Run with coverage report
pnpm --filter web test:coverage

# Run E2E tests
pnpm --filter e2e test

# Run specific test file
pnpm --filter web test:run path/to/file.test.ts

Test Scripts

The following scripts are available in the monorepo:

# Unit and integration tests
pnpm --filter web test           # Watch mode
pnpm --filter web test:run       # Single run
pnpm --filter web test:ui        # Vitest UI
pnpm --filter web test:coverage  # With coverage

# E2E tests
pnpm --filter e2e test           # Run all E2E tests
pnpm --filter e2e test --headed  # With visible browser
pnpm --filter e2e test --ui      # Playwright UI mode
pnpm --filter e2e test --debug   # Debug mode

Test Organization

File Structure

Tests are organized alongside source files:

apps/web/
├── app/
│   └── feature/
│       ├── _lib/
│       │   ├── server/
│       │   │   ├── server-actions.ts
│       │   │   └── __tests__/
│       │   │       └── server-actions.test.ts
│       │   └── schemas/
│       └── _components/
│           ├── my-component.tsx
│           └── __tests__/
│               └── my-component.test.tsx
├── core/
│   └── domain/
│       └── project/
│           ├── project.entity.ts
│           └── __tests__/
│               └── project.entity.test.ts
└── lib/
    └── utils/
        ├── helpers.ts
        └── __tests__/
            └── helpers.test.ts

E2E Test Structure

E2E tests are organized by feature:

apps/e2e/
├── tests/
│   ├── authentication/
│   │   ├── auth.po.ts          # Page Object
│   │   └── auth.spec.ts        # Test specs
│   ├── projects/
│   │   ├── projects.po.ts
│   │   └── projects.spec.ts
│   └── team-accounts/
│       ├── team-accounts.po.ts
│       └── team-accounts.spec.ts
└── playwright.config.ts

Testing Best Practices

1. Test Behavior, Not Implementation

// ✅ CORRECT - Test behavior
it('should calculate project area from geometry', () => {
  const project = Project.create({ geometry: mockPolygon });
  expect(project.calculateArea()).toBeCloseTo(100.5);
});

// ❌ WRONG - Test implementation details
it('should call _calculatePolygonArea method', () => {
  const project = Project.create({ geometry: mockPolygon });
  expect(project._calculatePolygonArea).toHaveBeenCalled();
});

2. Use Descriptive Test Names

// ✅ CORRECT - Descriptive names
describe('Project.archive()', () => {
  it('should set status to archived when project is active', () => {});
  it('should throw error when project is already archived', () => {});
  it('should update the updatedAt timestamp', () => {});
});

// ❌ WRONG - Vague names
describe('Project', () => {
  it('should work', () => {});
  it('test archive', () => {});
});

3. Avoid Mocking When Possible

// ✅ CORRECT - Test real behavior
it('should validate email format', () => {
  const result = Email.create('invalid-email');
  expect(result.ok).toBe(false);
});

// ❌ AVOID - Excessive mocking
it('should validate email', () => {
  const mockValidator = jest.fn().mockReturnValue(false);
  // This doesn't test real validation
});

4. Test Edge Cases

describe('Hectares.create()', () => {
  it('should accept zero', () => {
    const result = Hectares.create(0);
    expect(result.ok).toBe(true);
  });

  it('should reject negative values', () => {
    const result = Hectares.create(-1);
    expect(result.ok).toBe(false);
  });

  it('should handle very large values', () => {
    const result = Hectares.create(Number.MAX_SAFE_INTEGER);
    expect(result.ok).toBe(true);
  });
});

CI/CD Integration

Tests run automatically in the CI/CD pipeline:

  1. Pre-commit - Lint and type checking
  2. Pull Request - Full test suite
  3. Merge to main - Full test suite + E2E
  4. Deploy - Smoke tests in staging

GitHub Actions Example

name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v4
      
      - run: pnpm install
      - run: pnpm typecheck
      - run: pnpm --filter web test:run
      - run: pnpm --filter e2e test

Debugging Failed Tests

Unit Tests

# Run with verbose output
pnpm --filter web test:run --reporter=verbose

# Run specific test
pnpm --filter web test:run --grep "should calculate area"

# Use Vitest UI for debugging
pnpm --filter web test:ui

E2E Tests

# Run with visible browser
pnpm --filter e2e test --headed

# Debug mode (step through)
pnpm --filter e2e test --debug

# View test traces
pnpm --filter e2e test --trace on

Next Steps

On this page