Initial commit: nick docs
This commit is contained in:
262
07 - Development/Testing.md
Normal file
262
07 - Development/Testing.md
Normal file
@@ -0,0 +1,262 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user