> ## Documentation Index
> Fetch the complete documentation index at: https://docs.charle.agency/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Testing strategy and tools in CharleOS

CharleOS uses a comprehensive testing strategy with **Vitest** for unit tests and **Cypress** for E2E tests. This ensures code quality and prevents regressions.

## Testing Stack

<CardGroup cols={2}>
  <Card title="Unit Tests" icon="flask">
    **Vitest** - Fast unit testing for utilities and business logic

    * Tests in `__tests__/`
    * Run with `npm run test`
    * Coverage reports included
  </Card>

  <Card title="E2E Tests" icon="robot">
    **Cypress** - Browser automation for critical user flows

    * Tests in `cypress/e2e/`
    * Run with `npm run test:e2e`
    * Dedicated test users for isolation
  </Card>
</CardGroup>

## Test Coverage

### What We Test

**Unit Tests** (`__tests__/`):

* Business logic (billing calculations, time utilities)
* Data transformations
* Helper functions
* Type utilities

**E2E Tests** (`cypress/e2e/`):

* Authentication flows
* Critical user journeys (create task, approve quote)
* Client portal functionality
* Role-based access control

**What We Don't Test:**

* UI styling/layout
* Third-party libraries
* Database migrations
* Simple CRUD operations

## Running Tests

### Unit Tests

```bash theme={null}
# Run all unit tests
npm run test

# Run in watch mode (auto-rerun on changes)
npm run test:watch

# Run with coverage report
npm run test:coverage

# Run specific test file
npm run test __tests__/billing.test.ts
```

### E2E Tests

```bash theme={null}
# Seed test data first (if not already seeded)
npm run db:seed:e2e

# Run E2E tests (headless)
npm run test:e2e

# Run with Cypress UI (interactive)
npm run test:e2e:open

# Run specific test file
npx cypress run --spec "cypress/e2e/auth.cy.ts"
```

## Unit Test Examples

### Testing Utilities

```typescript theme={null}
// __tests__/billing.test.ts
import { describe, it, expect } from "vitest";
import { calculateBillable } from "@/lib/utils/billing";

describe("calculateBillable", () => {
  it("returns minimum when actual is below minimum", () => {
    const result = calculateBillable({
      actual: 100,
      average: 150,
      maximum: 200,
    });

    expect(result).toBe(150);  // Uses average (minimum)
  });

  it("returns maximum when actual exceeds maximum", () => {
    const result = calculateBillable({
      actual: 250,
      average: 150,
      maximum: 200,
    });

    expect(result).toBe(200);  // Capped at maximum
  });

  it("returns actual when within range", () => {
    const result = calculateBillable({
      actual: 175,
      average: 150,
      maximum: 200,
    });

    expect(result).toBe(175);  // Uses actual
  });
});
```

### Testing Date Utilities

```typescript theme={null}
// __tests__/working-days.test.ts
import { describe, it, expect } from "vitest";
import { calculateWorkingDays, isWorkingDay } from "@/lib/utils/working-days";

describe("isWorkingDay", () => {
  it("returns true for Monday-Friday", () => {
    const monday = new Date("2024-01-22");  // Monday
    expect(isWorkingDay(monday)).toBe(true);
  });

  it("returns false for Saturday-Sunday", () => {
    const saturday = new Date("2024-01-20");  // Saturday
    expect(isWorkingDay(saturday)).toBe(false);
  });
});

describe("calculateWorkingDays", () => {
  it("counts working days correctly", () => {
    const startDate = new Date("2024-01-22");  // Monday
    const endDate = new Date("2024-01-26");    // Friday

    const workingDays = calculateWorkingDays(startDate, endDate);

    expect(workingDays).toBe(5);  // Mon-Fri
  });

  it("excludes weekends", () => {
    const startDate = new Date("2024-01-22");  // Monday
    const endDate = new Date("2024-01-28");    // Sunday (next week)

    const workingDays = calculateWorkingDays(startDate, endDate);

    expect(workingDays).toBe(5);  // Only Mon-Fri
  });
});
```

## E2E Test Examples

### Authentication Flow

```typescript theme={null}
// cypress/e2e/auth.cy.ts
describe("Authentication", () => {
  it("redirects unauthenticated users to sign-in", () => {
    cy.visit("/clients");
    cy.url().should("include", "sign-in");
  });

  describe("Authenticated - Admin", () => {
    beforeEach(() => {
      cy.loginAsAdmin();
    });

    it("can access dashboard", () => {
      cy.visit("/");
      cy.url().should("not.include", "sign-in");
    });

    it("can access admin settings", () => {
      cy.visit("/admin/settings");
      cy.url().should("include", "/admin/settings");
    });
  });
});
```

## Test Configuration

### Vitest Config

```typescript theme={null}
// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "node",
    include: ["__tests__/**/*.test.ts"],
    setupFiles: ["__tests__/setup.ts"],
    coverage: {
      provider: "v8",
      reporter: ["text", "json", "html"],
      include: ["lib/**/*.ts", "app/api/**/*.ts"],
      exclude: ["lib/db/**", "lib/auth*.ts"],
      thresholds: {
        statements: 60,
        branches: 50,
        functions: 60,
        lines: 60,
      },
    },
  },
});
```

### Cypress Config

```typescript theme={null}
// cypress.config.ts
import { defineConfig } from "cypress";

export default defineConfig({
  e2e: {
    baseUrl: "http://localhost:3000",
    supportFile: "cypress/support/e2e.ts",
  },
});
```

## CI/CD Integration

Tests run automatically on every pull request:

```yaml theme={null}
# .github/workflows/ci.yml
jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Run unit tests
        run: npm run test:coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3

  e2e-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Seed E2E data
        run: npm run db:seed:e2e

      - name: Run E2E tests
        run: npm run test:e2e
```

## Best Practices

<AccordionGroup>
  <Accordion title="Test Business Logic, Not Implementation" icon="bullseye">
    Focus on behavior, not internal details:

    ```typescript theme={null}
    // Good: Test behavior
    it("calculates billable time correctly", () => {
      const result = calculateBillable({ actual: 175, average: 150, max: 200 });
      expect(result).toBe(175);
    });

    // Bad: Test implementation
    it("uses Math.min and Math.max", () => {
      // Testing internal implementation details
    });
    ```
  </Accordion>

  <Accordion title="Keep Tests Fast" icon="bolt">
    Unit tests should run in milliseconds:

    * No database calls in unit tests
    * Mock external dependencies
    * Use in-memory data
  </Accordion>

  <Accordion title="Use Descriptive Test Names" icon="file-signature">
    ```typescript theme={null}
    // Good: Clear and specific
    it("returns average when actual is below average", () => { ... });

    // Bad: Vague
    it("works correctly", () => { ... });
    ```
  </Accordion>

  <Accordion title="Test Edge Cases" icon="diamond">
    Don't just test the happy path:

    ```typescript theme={null}
    describe("calculateWorkingDays", () => {
      it("handles same start and end date");
      it("handles start date after end date");
      it("handles weekends");
      it("handles single day");
      it("handles bank holidays");
    });
    ```
  </Accordion>
</AccordionGroup>

## Debugging Tests

### Vitest

```bash theme={null}
# Run tests with debugging
npm run test -- --inspect-brk

# Run specific test
npm run test -- -t "calculates billable time"

# Show console.log output
npm run test -- --reporter=verbose
```

### Cypress

```bash theme={null}
# Run with Cypress UI
npm run test:e2e:open

# Run specific test
npx cypress run --spec "cypress/e2e/auth.cy.ts"

# Run in headed mode
npx cypress run --headed
```

## Coverage Reports

```bash theme={null}
# Generate coverage report
npm run test:coverage

# Open HTML report
open coverage/index.html
```

**Coverage Thresholds:**

* **Statements**: 60%
* **Branches**: 50%
* **Functions**: 60%
* **Lines**: 60%

## Related Documentation

<CardGroup cols={2}>
  <Card title="Unit Tests" icon="flask" href="/developer/testing/unit-tests">
    Writing unit tests with Vitest
  </Card>

  <Card title="E2E Tests" icon="robot" href="/developer/testing/e2e-tests">
    Writing E2E tests with Cypress
  </Card>

  <Card title="CI/CD" icon="circle-check" href="/developer/deployment/ci-cd">
    Automated testing in CI
  </Card>
</CardGroup>
