263 lines
9.1 KiB
Markdown
263 lines
9.1 KiB
Markdown
---
|
|
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.
|
|
|
|
---
|
|
|
|
## 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
|
|
- `payment-services.test.ts` — DePay, SHKeeper, Web3, admin operations
|
|
- `complete-backend.test.ts` — Auth, marketplace, chat, notification, address, user, file, email, AI
|
|
- `shkeeper-backend.test.ts` — Service layer + API endpoints for SHKeeper
|
|
|
|
### 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 (each maps to a single file):
|
|
npm run test:models # jest __tests__/models.test.ts
|
|
npm run test:payment # jest __tests__/payment-services.test.ts
|
|
npm run test:complete # jest __tests__/complete-backend.test.ts
|
|
npm run test:shkeeper # jest __tests__/shkeeper-backend.test.ts
|
|
```
|
|
|
|
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 (SHKeeper, OpenAI) with `jest.spyOn(axios, 'post')`. 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.
|