# Testing Patterns

**Analysis Date:** 2026-05-21

## Test Framework

**Runner:**
- Jest, provided transitively by `react-scripts@5.0.1` (Create React App).
- No standalone `jest.config.*`, `vitest.config.*`, or `jest` section in `package.json` — all configuration is the CRA default.

**Assertion Library:**
- Jest built-ins (`expect`, matchers).
- `@testing-library/jest-dom@^5.11.4` — adds DOM matchers like `toBeInTheDocument()`, `toHaveTextContent()`. Globally loaded via `src/setupTests.js`.
- `@testing-library/react@^11.1.0` — provides `render`, `screen`, and React-aware queries.
- `@testing-library/user-event@^12.1.10` — installed for simulating user interactions (not yet used in any test).

**Test setup file:** `src/setupTests.js`
```js
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
```
CRA auto-loads this file before every test run. Add global mocks / matchers here.

**Run Commands:**
```bash
yarn test                  # Run tests in interactive watch mode (CRA default)
yarn test --watchAll=false # Run once and exit (use in CI)
yarn test --coverage       # Generate coverage report under coverage/
```
`package.json` exposes only `"test": "react-scripts test"`. There are no CI-specific scripts.

## Test File Organization

**Location:**
- Co-located with the file under test. CRA picks up any file matching `**/*.{test,spec}.{js,jsx,ts,tsx}` under `src/`.

**Naming:**
- `<ComponentOrModule>.test.js` next to the source file. Example: `src/App.test.js` next to `src/App.js`.
- `.spec.js` is supported by Jest but not used anywhere in this repo.

**Current state of the test suite:**
- There is **exactly one test file** in the entire codebase: `src/App.test.js`.
- That test is the unmodified CRA template (`renders learn react link`) and will fail against the actual `App.js` (which does not render any "learn react" text). Treat the suite as non-functional baseline — running `yarn test` does not gate any code path today.

**Structure (CRA default file layout):**
```
src/
├── App.js
├── App.test.js          # only test file
├── setupTests.js        # jest-dom import
├── components/
│   └── <Component>/     # no co-located tests
├── screens/
│   └── <Screen>/        # no co-located tests
├── bigOrders/           # no tests
├── scans/               # no tests
├── utils/               # no tests
└── mocks/
    └── trainers.js      # static mock data (not Jest mocks)
```

## Test Structure

**Suite Organization:**
The single existing test uses Jest's top-level `test()` form, not `describe()` blocks:

```js
// src/App.test.js
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});
```

**Recommended pattern for new tests** (to match what already exists and CRA conventions):
- Use `test('...', () => { ... })` for one-off cases.
- Use `describe('Component', () => { test(...) })` when you have multiple cases for one unit.
- Use `@testing-library/react`'s `render` + `screen.getByText/Role/LabelText/...` queries — do NOT use Enzyme (not installed) and do NOT query the DOM directly with `document.querySelector`.

**Setup / teardown:**
- No `beforeEach` / `afterEach` patterns are established. The CRA test environment auto-cleans the DOM between tests via `@testing-library/react` v11's default behavior.

## Mocking

**Framework:** Jest's built-in `jest.fn()`, `jest.mock()`, `jest.spyOn()` are available via `react-scripts`. None are currently used.

**Patterns to use when adding tests:**
The app's data layer is `BigApi` (`src/api.js`), a class with static async methods that wrap `superagent`. Mock at the `BigApi` boundary, not at `superagent`:

```js
// Example pattern (not yet present in repo)
import { BigApi } from './api';
jest.mock('./api', () => ({
  BigApi: {
    get: jest.fn().mockResolvedValue({ body: { username: 'tester' } }),
    getRdcUrl: jest.fn().mockResolvedValue({ url: 'https://rdc.example' }),
  },
  BigApiError: class extends Error {},
}));
```

**Things that must be mocked or stubbed in component tests:**
- `BigApi.*` calls fire in many components' `useEffect` (`src/App.js:96-112`, `src/components/Header/index.js:112-121`, `src/bigOrders/BigOrders.js:18-45`, `src/scans/ScanStartPage.js:20-42`). Mock the whole `../api` module.
- `react-ga` — `ReactGA.initialize(...)` runs at module load in `src/App.js:61` and `src/BeyondApp.js:9`. Mock it if you import these top-level files.
- `@builder.io/react` — `builder.init(...)` runs in `src/index.js`. Do not import `src/index.js` from tests.
- `window.navigator.userAgent` is read in `src/scans/ScanStartPage.js:7-12` (Safari / iOS checks) — stub with `Object.defineProperty(window.navigator, 'userAgent', { value: '...', configurable: true })`.
- `document.cookie` is used by `BigApi.setCookie/getCookie/deleteCookie` (`src/api.js:101-123`). JSDOM supports `document.cookie` natively, so usually no mock needed; reset between tests with `document.cookie.split(';').forEach(c => document.cookie = c.split('=')[0] + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;')`.
- `process.env.REACT_APP_*` env vars are read at class-load time in `src/api.js:72-77` and `src/config.js`. Set them in `src/setupTests.js` or before the import.

**What NOT to mock:**
- React Router — wrap components under test in `<MemoryRouter>` from `react-router-dom` rather than mocking the router.
- `classnames` — pure utility, no need to mock.
- `lodash` — pure utility, no need to mock.

## Fixtures and Factories

**Test Data:**
- No formal test fixtures / factories exist.
- `src/mocks/trainers.js` is **static mock data for runtime use** (a content array), not Jest mocks. Do not repurpose it as a fixtures directory without renaming.

**Recommended location for new fixtures:**
- Co-locate small fixtures inline in the `.test.js` file.
- For shared fixtures, create `src/__fixtures__/<name>.js` (Jest auto-recognizes `__fixtures__` as a non-test directory) or a `src/test-utils/` folder.

## Coverage

**Requirements:**
- No coverage thresholds configured. No `jest.config.coverageThreshold`, no CI enforcement, no badges.

**View Coverage:**
```bash
yarn test --coverage --watchAll=false
```
Output appears in `coverage/lcov-report/index.html`. The `coverage/` directory is not in `.gitignore` by default — if you generate coverage, do not commit the report.

## Test Types

**Unit Tests:**
- Approach when added: render a single component with `@testing-library/react`, mock `../api` and any other side-effect modules, assert via `screen.getBy*` + `jest-dom` matchers.
- Pure utility modules (`src/utils/formatters.js`, `src/utils/errorget.js`, `src/utils/countryformat.js`, `src/utils/languagecodeformat.js`) are the highest-leverage unit-test targets — they have no React/dependency surface and use CommonJS `exports.fn = ...`, so they can be tested with a plain `const { formatPrice } = require('./formatters');`.

**Integration Tests:**
- Not present. The natural integration boundary is `<App />` under a `<MemoryRouter initialEntries={[...]}>` with `BigApi` mocked — but `src/App.test.js` does not yet do this.

**E2E Tests:**
- Not used. No Cypress, Playwright, or Puppeteer in `package.json`.

## Common Patterns

**Async Testing (recommended pattern, not yet used):**
```js
import { render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { BigApi } from '../api';

jest.mock('../api');

test('shows username after account loads', async () => {
  BigApi.get.mockResolvedValue({ body: { username: 'alice' } });
  BigApi.getRdcUrl.mockResolvedValue({ url: '' });

  render(
    <MemoryRouter initialEntries={['/']}>
      <App />
    </MemoryRouter>
  );

  await waitFor(() => {
    expect(screen.getByText(/alice/i)).toBeInTheDocument();
  });
});
```

**Error Testing (recommended pattern):**
```js
import { BigApiError } from '../api';

test('BigApiError carries status and code from response body', () => {
  const fakeErr = {
    response: { status: 422, body: { code: 7, message: 'Invalid login' } }
  };
  const e = BigApiError.getApiError(fakeErr);
  expect(e).toBeInstanceOf(BigApiError);
  expect(e.status).toBe(422);
  expect(e.code).toBe(7);
  expect(e.message).toBe('Invalid login');
});
```

**Router-aware Testing:**
- Always wrap components that use `useNavigate`, `useLocation`, `useParams`, `useSearchParams`, `<Link>`, or `<NavLink>` in `<MemoryRouter>`. Direct render without a router will throw.

## Known Gaps

- `src/App.test.js` is the only test and is stale (asserts on "learn react" text that does not exist in current `src/App.js`). It will fail if run today.
- No tests for `BigApi` (the most security-critical module), no tests for any component, screen, util, or hook.
- No CI is configured to run `yarn test`, so the broken `App.test.js` does not currently block anything.
- When writing the first real test, also delete or rewrite `src/App.test.js` so `yarn test` exits clean.

---

*Testing analysis: 2026-05-21*
