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

# Testing

> How to write and run unit tests with Vitest and E2E tests with Playwright.

## Overview

folksbase uses two testing layers:

* **Vitest** for unit tests — fast, focused tests for business logic
* **Playwright** for E2E tests — browser-based tests against the full running app

## Unit Tests (Vitest)

### Where Tests Live

Tests go in `__tests__/` directories next to the file being tested:

```
src/
├── services/
│   ├── contacts.service.ts
│   └── __tests__/
│       └── contacts.service.test.ts
├── repositories/
│   ├── contacts.repository.ts
│   └── __tests__/
│       └── contacts.repository.test.ts
```

### Running Tests

```bash theme={"dark"}
# Run all unit tests
pnpm test

# Run tests for a specific package
pnpm --filter @folksbase/api test

# Run a single test file
pnpm --filter @folksbase/api test src/services/__tests__/csv-ai.service.test.ts
```

### Writing Tests

Mock all external dependencies. Tests should be fast and isolated — no real database, Redis, or API calls.

```typescript theme={"dark"}
import { describe, it, expect, vi, beforeEach } from "vitest";

// Mock external dependencies
vi.mock("@folksbase/db");
vi.mock("@/lib/redis.js");

describe("contacts.service", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("creates a contact with normalized email", async () => {
    // Arrange
    const mockCreate = vi.fn().mockResolvedValue({ id: "abc", email: "test@example.com" });
    // ... setup mocks

    // Act
    const result = await createContact({ email: "TEST@Example.COM" });

    // Assert
    expect(result.email).toBe("test@example.com");
    expect(mockCreate).toHaveBeenCalledOnce();
  });
});
```

### What to Test

* Business logic in services (happy path + error cases)
* Repository query behavior (mocked DB responses)
* Edge cases: empty inputs, missing data, concurrent operations
* Error handling: verify errors propagate correctly

### What NOT to Test

* Drizzle query builder syntax — trust the ORM
* Zod schema validation — trust Zod
* Framework internals — trust Next.js and Hono

### Test Configuration

Vitest config lives in each app's `vitest.config.ts`. The API config sets up path aliases so imports like `@/lib/logger.js` and `@folksbase/db` resolve correctly in tests:

```typescript theme={"dark"}
resolve: {
  alias: {
    "@": resolve(__dirname, "src"),
    "@folksbase/db": resolve(__dirname, "../../packages/db/src/index.ts"),
    "@folksbase/types": resolve(__dirname, "../../packages/types/src/index.ts"),
  },
},
```

### Important: Use Valid UUIDs in Tests

Route param validators enforce UUID format. Always use valid UUIDs in test fixtures, not arbitrary strings:

```typescript theme={"dark"}
// ❌ Will fail validation
const contactId = "c-1";

// ✅ Use a real UUID
const contactId = "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
```

## E2E Tests (Playwright)

### Where Tests Live

E2E tests live in `apps/web/e2e/` with config in `apps/web/playwright.config.ts`.

### Test Projects

Playwright is configured with three projects:

| Project    | Purpose                                                     |
| ---------- | ----------------------------------------------------------- |
| `setup`    | Authenticates once, stores session in `e2e/.auth/user.json` |
| `no-auth`  | Runs `auth.spec.ts` with empty storage (tests login flow)   |
| `chromium` | Runs all other specs with stored auth (depends on `setup`)  |

### Running E2E Tests

```bash theme={"dark"}
# Run all E2E tests
pnpm --filter @folksbase/web e2e

# Run with Playwright UI (interactive mode)
pnpm --filter @folksbase/web e2e:ui

# Run a single spec
pnpm --filter @folksbase/web exec playwright test e2e/auth.spec.ts --project=no-auth
```

### Environment Variables

E2E tests require real Supabase credentials:

| Variable              | Purpose                                       |
| --------------------- | --------------------------------------------- |
| `E2E_USER_EMAIL`      | Test user email for authentication            |
| `E2E_USER_PASSWORD`   | Test user password                            |
| `PLAYWRIGHT_BASE_URL` | App URL (defaults to `http://localhost:3000`) |

These are defined in `apps/web/.env.example` and read via `process.env` in the Playwright config.

### Test Files

The E2E suite covers: `auth`, `dashboard`, `navigation`, `contacts`, `imports`, `exports`, `tags`, `settings`, and `accessibility`.

### CI Integration

In CI, E2E tests run against the Vercel preview URL generated for each PR. The `PLAYWRIGHT_BASE_URL` is overridden to point to the preview deployment.

## Storybook

UI components are documented with Storybook 8. Stories live alongside their components as `*.stories.tsx` files.

```bash theme={"dark"}
pnpm --filter @folksbase/web storybook        # Run locally on port 6006
pnpm --filter @folksbase/web build-storybook  # Build static output
```

When adding a new UI component, include a story file covering its variants and states. Storybook auto-deploys to Netlify on pushes to `main` when component or config files change.
