# Testing React components in ScalesApp ## What's available - **Vitest + jsdom** (`npm test` → `vitest run`). Run a single suite with: `npx vitest run `. Use `vi.*` (not `jest.*`); `globals: true` is set, so `describe`/`test`/`expect`/`vi` need no import. See [[vite-vitest-migration]]. - **`@testing-library/react` is NOT installed.** Don't assume it; mount with `createRoot` from `react-dom/client` and `act` from `react` instead. - There is no `src/setupTests.js`. Set `global.IS_REACT_ACT_ENVIRONMENT = true` in the test file itself, or React logs "testing environment is not configured to support act(...)". - Import `act` from `react`, not `react-dom/test-utils` (the latter is deprecated in React 18 and prints a warning on every call). ## Three gotchas that produce fake failures These each look like a component bug and are not. Verify with a tiny probe test before "fixing" component code. 1. **React's `onBlur`/`onFocus` map to native `focusout`/`focusin`.** Dispatching `new FocusEvent('blur')` does nothing — the handler never runs, so the component looks broken. Dispatch `focusout`/`focusin` instead. 2. **`MouseEvent` coerces `clientX`/`clientY` to integers.** Passing a fractional pixel coordinate silently truncates. In a mm-based editor that shows up as a ~0.3mm discrepancy that looks like a rounding bug in the conversion code. Use whole pixels in tests and derive expected values from those same integers. 3. **`` in jsdom returns `""` for a partial value like `"12."`.** Real browsers sanitise similarly. See [[decimal-inputs]]. ## jsdom has no layout `getBoundingClientRect()` returns all zeros. For canvas/positioning components, stub it per test to pin a known origin: ```js Element.prototype.getBoundingClientRect = function () { if (this.classList.contains('a4-page')) { return { left: 100, top: 50, /* ...*/ toJSON() {} }; } return { left: 0, top: 0, /* ... */ toJSON() {} }; }; ``` ## Driving controlled inputs React overrides the `value` setter, so `input.value = x` alone won't trigger `onChange`. Use the native setter, then dispatch `input`: ```js const setter = Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, 'value' ).set; setter.call(input, '12.'); input.dispatchEvent(new Event('input', { bubbles: true })); ``` ## Backend round-trips without a live server Outbound HTTP from the agent sandbox may be blocked even when the dev server is running. To exercise real DRF serializers/filters/DB, use Django's test client directly against the dev database — pass `APIClient(SERVER_NAME='localhost')`, since the default `testserver` host is rejected by `ALLOWED_HOSTS`.