Files
nick-doc/07 - Development/Testing.md
2026-05-23 20:35:34 +03:30

9.1 KiB

title, tags
title tags
Testing
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

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 --:

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:
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);
  });
});
  1. Use the in-memory DB — connections are wired in setup.ts. Each test starts with a clean collection.
  2. 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

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)
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:
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();
});
  1. Mock lib/axios if the component makes API calls:
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:
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/);
});
  1. Run yarn test:e2e:headed to debug.
  2. 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:

- 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.matchMediajest.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.