---
title: Testing
tags: [development]
---

# Testing

Both repos use **Jest** as the unit/integration runner. The frontend additionally uses **React Testing Library** for component tests and **Playwright** for end-to-end browser tests. This page covers what exists today, how to run it, and how to add new tests.

For cross-service procedures, live-dev E2E scenarios, scanner payment runs, CI
verification, and release-blocking test gaps, see [[11 - Testing/Testing Overview]].

---

## Backend testing

### Stack

- **Jest 29** + **ts-jest 29** — TypeScript transpilation on the fly
- **supertest 7** — HTTP assertions against the Express app
- **mongodb-memory-server 10** — in-memory MongoDB per test run (no Docker needed)

### Jest configuration

`backend/jest.config.js`:

- Preset: `ts-jest`
- Environment: `node`
- Test glob: `**/__tests__/**/*.test.ts` and `**/?(*.)+(spec|test).ts`
- `setupFilesAfterEach`: `__tests__/setup.ts` — boots `mongodb-memory-server`, connects mongoose, and cleans collections between tests
- `maxWorkers: 1` — tests run serially (DB-state-sensitive)
- `testTimeout: 30000`

### Test suites

`backend/__tests__/` contains:

| File | What it covers |
|------|----------------|
| `basic.test.ts` | Smoke test of the bootstrap |
| `file-service.test.ts` | Upload + Sharp image-processing pipeline |
| `payment-integration.test.ts` | End-to-end pay-in / pay-out across providers |
| `payment-system.test.ts` | Payment service unit tests |
| `shkeeper-webhook.test.ts` | Signature verification + status transition |
| `simple-marketplace.test.ts` | Purchase-request + offer flow |
| `simple-payment.test.ts` | Single-provider payment fast-path |
| `simple-user.test.ts` | Auth + signup + JWT issuance |
| `setup.ts` | Shared Jest setup (DB, env vars, helpers) |

There are also four large aggregate suites referenced in `package.json` (some may live in branches or be reintroduced as the codebase evolves):

- `models.test.ts` — every Mongoose schema, validation, indexes, relationships
- `request-network-adapter.test.ts`, `request-network-webhook.test.ts`, `rn-in-house-checkout.test.ts` — Request Network checkout and webhook behavior
- `payment-ledger.service.test.ts`, `payment-release-refund-orchestration.test.ts` — ledger and release/refund behavior
- `complete-backend.test.ts` — Auth, marketplace, chat, notification, address, user, file, email, AI

### Commands

```bash
cd ~/code/backend

npm run test              # run all *.test.ts files once (forceExit on)
npm run test:watch        # interactive watch mode
npm run test:coverage     # also emit coverage report to ./coverage/
npm run test:all          # explicit __tests__/ folder

# Focused suites:
npm run test:models       # jest __tests__/models.test.ts
npm run test:complete     # jest __tests__/complete-backend.test.ts
npm run test -- --testPathPattern=request-network
npm run test -- --testPathPattern=payment-ledger
```

Pass extra Jest flags after `--`:

```bash
npm run test -- --testPathPattern=payment --verbose
```

### Coverage targets

- Statements & lines: **≥ 80 %** on changed files
- Branches: **≥ 70 %** on changed files
- Critical paths (auth, payment, escrow release) — aim for **≥ 90 %**

Coverage is collected from `src/**/*.ts` excluding `.d.ts` and `__tests__/`. View the HTML report at `coverage/lcov-report/index.html` after running `npm run test:coverage`.

### Adding a new backend test

1. Place file under `__tests__/` (or colocated `*.test.ts` next to the source).
2. Import the app and use `supertest`:

```ts
import request from 'supertest';
import { app } from '../src/app';

describe('GET /api/health', () => {
  it('returns 200', async () => {
    const res = await request(app).get('/health');
    expect(res.status).toBe(200);
    expect(res.body.success).toBe(true);
  });
});
```

3. Use the in-memory DB — connections are wired in `setup.ts`. Each test starts with a clean collection.
4. Mock outbound HTTP (Request Network, OpenAI, AML providers) with `jest.spyOn(axios, 'post')` or a dedicated adapter mock. Never hit a real provider from tests.

> [!warning] `maxWorkers: 1` makes tests serial. Don't introduce timing-sensitive parallelism — instead, keep individual tests small and deterministic.

---

## Frontend testing

### Stack

- **Jest 29** + **ts-jest 29** + **jsdom 29** — component & util tests
- **@testing-library/react 16** + **@testing-library/jest-dom 6** + **@testing-library/user-event 14**
- **Playwright 1.56** — browser-driven E2E
- **identity-obj-proxy** — stubs CSS module imports

### Jest configuration

`frontend/jest.config.js`:

- Environment: `jsdom`
- Roots: `<rootDir>/src` and `<rootDir>/__tests__`
- Test globs: `**/__tests__/**/*.test.(ts|tsx|js)`, `**/__tests__/**/*.spec.(ts|tsx|js)`, `**/*.(test|spec).(ts|tsx|js)`
- Asset mapping: images & fonts → `__tests__/mocks/fileMock.js`, CSS → `identity-obj-proxy`
- Module aliases: `src/*` → `<rootDir>/src/*`
- `setupFilesAfterEach`: `jest.setup.js` (sets up RTL matchers, axios mocks, env)

### Test layout — `__tests__/`

Tests are grouped by domain:

```
__tests__/
├── account-test/
├── address-test/
├── auth-test/
├── chat-test/
├── components-test/
├── file-test/
├── integration-test/
├── marketplace-test/
├── notification-test/
├── payment-test/
├── user-test/
├── utils-test/
└── mocks/
```

Each folder contains one or more `*.test.tsx` files. See `__tests__/README.md`, `PROJECT_TEST_CHECKLIST.md`, and `TEST_ORGANIZATION_SUMMARY.md` in the repo for current status.

### Commands

```bash
cd ~/code/frontend

yarn test                      # full Jest suite
yarn test -- --watch           # watch mode
yarn test -- --coverage        # coverage report
yarn test -- payment           # name-pattern filter
```

### Playwright E2E

`frontend/e2e/` contains four headless-Chromium suites:

| File | Coverage |
|------|----------|
| `auth.spec.ts` | Sign up, login, logout, password reset |
| `marketplace.spec.ts` | Browse, create request, accept offer |
| `shop.spec.ts` | Public shop pages |
| `performance.spec.ts` | Performance budgets (LCP, INP) |

```bash
yarn test:e2e                  # headless run
yarn test:e2e:ui               # open Playwright Inspector
yarn test:e2e:headed           # show the browser
yarn test:e2e:debug            # step through with devtools
yarn test:perf                 # only e2e/performance.spec.ts
yarn playwright:install        # one-time browser download
```

Playwright assumes the backend + frontend are reachable at the URLs in `playwright.config.ts` (defaults: `http://localhost:8083` and `http://localhost:5001`). Start both stacks first — see [[Local Setup]].

### Coverage targets (frontend)

- Components: **≥ 70 %** statement coverage
- Hooks / utilities: **≥ 90 %**
- Critical flows (login, checkout): covered by both unit and Playwright suites

### Adding a new component test

1. Colocate `MyComponent.test.tsx` next to `MyComponent.tsx`, or place in `__tests__/<domain>-test/`.
2. Render with RTL and assert via accessible queries:

```tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ThemeProvider } from 'src/theme/theme-provider';
import { MyComponent } from './MyComponent';

const renderWithProviders = (ui: React.ReactElement) =>
  render(<ThemeProvider>{ui}</ThemeProvider>);

it('submits the form', async () => {
  renderWithProviders(<MyComponent />);
  await userEvent.type(screen.getByLabelText(/email/i), 'a@b.com');
  await userEvent.click(screen.getByRole('button', { name: /submit/i }));
  expect(screen.getByText(/thanks/i)).toBeInTheDocument();
});
```

3. Mock `lib/axios` if the component makes API calls:

```ts
import api from 'src/lib/axios';
jest.mock('src/lib/axios');
const mockedApi = api as jest.Mocked<typeof api>;
mockedApi.post.mockResolvedValueOnce({ data: { success: true } });
```

### Adding a Playwright test

1. Add `e2e/<feature>.spec.ts`:

```ts
import { test, expect } from '@playwright/test';

test('user can log in', async ({ page }) => {
  await page.goto('/auth/jwt/sign-in');
  await page.getByLabel('Email').fill('admin@marketplace.com');
  await page.getByLabel('Password').fill('Moji6364');
  await page.getByRole('button', { name: 'Login' }).click();
  await expect(page).toHaveURL(/\/dashboard/);
});
```

2. Run `yarn test:e2e:headed` to debug.
3. Keep specs idempotent — clean up any test data the spec creates (or rely on the seeded test users).

---

## CI integration

The Gitea Actions workflows (see [[CI-CD Pipeline]]) currently build and push Docker images but do not yet run Jest. If you add test gating, add a `Run tests` step before the `Build` step:

```yaml
- name: Install
  run: yarn install --frozen-lockfile
- name: Test
  run: yarn test --ci --runInBand
```

---

## Debugging tips

- **Backend test hangs** — add `--detectOpenHandles --forceExit`. Almost always a forgotten `mongoose.disconnect()` or open Redis client. The shared `setup.ts` handles this, but custom suites might not.
- **Frontend test fails on `window.matchMedia`** — `jest.setup.js` polyfills it; if you add a new test runner config, copy that polyfill.
- **Playwright flaky** — use `await expect(...).toBeVisible()` rather than `waitForSelector` and increase per-test timeout in `playwright.config.ts` for slow flows.
- **Coverage low but the test exists** — make sure the file is in `collectCoverageFrom` and not excluded by an `index.ts` filter.
