--- name: vite-vitest-migration description: Frontend build is Vite + Vitest (not CRA/Jest) — the JSX-in-.js trap, why axios no longer needs stubbing, and the jsdom border assertion gotcha metadata: type: project --- The frontend migrated from Create React App / Jest to **Vite + Vitest**. Config lives in `frontend/vite.config.mjs` (`.mjs`, not `.js` — Vite's native config loader warns about ESM in a `.js` config). Replaces the old `jest-axios-esm` note, which is now obsolete. **Vitest transforms axios fine, so the old blanket `jest.mock('axios')` hack is gone.** Three of the six a4 suites dropped it entirely. `a4EntryPoints` still mocks axios, but for a *behavioural* reason: it drives the report auto-load via `api.get.mockResolvedValue(...)`, so the instance `services/api.js` builds at import time must be a mock. Distinguish the two reasons before deleting a mock. **The JSX-in-`.js` trap — this one fails at BUILD time only.** CRA's Babel parsed JSX inside `.js` files; Vite does not by default. Setting `oxc: { include: /\.(m?ts|[jt]sx|js)$/, lang: 'jsx' }` fixes the dev server and Vitest, but **not** `vite build`: the bundled path (Rolldown, `builtin:vite-transform`) derives the language from the file extension and ignores `lang`. So a `.js` file containing JSX will pass tests and the dev server, then fail the production build. **How to apply:** any new production file containing JSX must be named `.jsx`. The `oxc` override exists only so the existing a4 `__tests__/*.test.js` files keep working. Importers use extensionless specifiers, so renaming a file `.js` → `.jsx` needs no import changes (this is how `print/printReport.jsx` was renamed without touching the dynamic `import()` in `Main.jsx`). **Env vars are `import.meta.env.VITE_*`**, not `process.env.REACT_APP_*`; `process.env` does not exist in the browser. The `|| 'http://localhost:…'` fallbacks are the offline-deployment safety net — keep them. The dev-server port (5172, in `vite.config.mjs`) must match Django's `CORS_ALLOWED_ORIGINS`. Renaming these vars means touching `Dockerfile`, `docker-compose.yml`, `.env.docker` and the gitignored root `.env` too, or the deployed bundle silently falls back to localhost. **Newer jsdom round-trips `border: none`.** It now reports `borderStyle === 'none'` with `borderWidth === 'medium'` (the CSS initial keyword). Assert on **`borderStyle`**, not `borderWidth === ''` — the latter was written around an old-jsdom quirk and passed vacuously. Two `a4Elements` tests had to be corrected for this during the migration; the components were correct. **`global.IS_REACT_ACT_ENVIRONMENT = true` is no longer required** (Vitest's jsdom environment sets it), but it is harmless and still present in all six suites. Related: [[testing-react]], [[a4-print-path]], [[a4-entry-points]]