diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000000..ccaf2940fe --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,219 @@ +--- + +# GEMINI.md – Agent Playbook for Next.js Frontend + +PS! DO NOT RUN TESTS/BUILD/LINT IF NOT EXPLICITLY ASKED + +This document is the contract for any coding agent (e.g. codex‑cli) working in this repo. It describes **how to run checks**, **what “good” looks like**, and the **modernization rules** tied to our stack (Next.js 16 + React 19.2). + +--- + +## Quickstart + +### Setup + +```bash +npm install +``` + +### Test + +```bash +npm run test +``` + +### Lint & Format + +```bash +npm run lint +``` + +### Type Check + +```bash +npm run type-check +``` + +> **Note on Next.js 16 & ESLint:** Starting with Next 16, `next lint` is removed. Use the ESLint CLI driven by `eslint-config-next` (flat config). Remove any `eslint` options from `next.config.*`. ([Next.js][1]) + +--- + +## Programmatic Checks (must pass before completing any task) + +Run all the following (unless you are only editing docs or non-code, in which case tests may be skipped): + +```bash +npm run test +npm run lint +npm run type-check +``` + +For small changes, you may run a faster coverage subset: + +```bash +npm run test:cov:changed +``` + +* `npm run test`: Executes all Jest tests and enforces **≥ 80% line coverage for files changed since `main`**. Fails if tests fail or coverage threshold is not met. +* `npm run test:cov:changed`: Jest on changed files only; still enforces the 80% threshold. +* `npm run lint`: Code must satisfy ESLint (Next’s Core Web Vitals + React Hooks). +* `npm run type-check`: Must pass `tsc --noEmit`. + +If tests fail due to coverage, write meaningful tests until coverage ≥ 80%. If a test fails functionally, fix root cause (code or test) and re‑run until green. + +--- + +## MCP: Enable Next.js DevTools for Agents (highly recommended) + +Enable the **Next DevTools MCP server** so agents can query live routes, errors, logs, and Server Actions from a running `next dev`: + +```jsonc +// .mcp.json (project root) +{ + "mcpServers": { + "next-devtools": { + "command": "npx", + "args": ["-y", "next-devtools-mcp@latest"] + } + } +} +``` + +* When `next dev` is running, `next-devtools-mcp` auto‑discovers and connects to the app. +* Available tools include: `get_errors`, `get_logs`, `get_page_metadata`, `get_project_metadata`, and `get_server_action_by_id`. Agents should use them **before** changing code when fixes might affect routing, hydration, or Server Actions. ([Next.js][2]) + +--- + +## Agent Operating Principles + +1. **Fix with modernization** (no “make the warning go away”). Don’t add `// eslint-disable` unless explicitly instructed. Prefer refactors aligned with **React 19.2**, **React Compiler**, and **Next.js 16** conventions. ([React][3]) +2. **Prefer Server over Client** where possible. Data reads: Server Components with inline fetches. Mutations: Server Functions / Server Actions (`'use server'`). Avoid client Effects for data fetching unless truly needed. ([Next.js][4]) +3. **Effects are last resort.** If there’s no external system, remove the Effect and compute during render. If you must listen to external events but need the latest props/state without re‑running the Effect, use **`useEffectEvent`**. ([React][5]) +4. **Use framework APIs:** internal links → ``, images → `next/image`, and adopt Next’s ESLint rules (Core Web Vitals). ([Next.js][1]) +5. **Cache explicitly where it helps.** With Next 16, caching is **opt-in** via the `"use cache"` directive and related Cache Components features (see below). ([Next.js][6]) +6. **Commit small, surgical diffs.** If you uncover a broader refactor, open a follow‑up ticket rather than ballooning a lint‑fix PR. + +--- + +## Next.js 16: What this means for agents + +* **Proxy instead of Middleware:** `middleware.ts` is **renamed to** `proxy.ts` (Node runtime). If you touch request‑boundary logic, ensure the file and exported function are named `proxy`. Legacy `middleware.ts` still exists for edge‑only cases but our default is `proxy.ts`. ([Next.js][6]) +* **ESLint changes:** `next lint` removed; use ESLint CLI with `eslint-config-next` flat config. ([Next.js][1]) +* **React Compiler (stable):** Supported in Next 16. It **auto‑memoizes components**, reducing the need for manual `useMemo`/`useCallback`. You may see lints originating from the compiler surfaced via `eslint-plugin-react-hooks`. Consider enabling the compiler in `next.config.ts` when CI is green: + + ```ts + // next.config.ts + export default { reactCompiler: true } + ``` + + ([Next.js][6]) +* **Cache Components / `"use cache"`:** Caching is explicit. You can place `"use cache"` at the top of a Server Component, route, or function to opt-in caching; configure `cacheComponents: true` in `next.config.ts` as needed. Prefer tagging/expiration APIs over ad-hoc hacks. ([Next.js][7]) +* **Turbopack default:** Dev and build use Turbopack by default in v16—don’t pass `--turbopack`. ([Next.js][8]) + +--- + +## React 19.2: Effects guidance for agents + +* **Remove unnecessary Effects.** If the Effect’s only job is to derive or sync internal state, calculate during render or use `useMemo` if truly expensive (the **Compiler** may remove that need). ([React][5]) +* **Use `useEffectEvent`** for non‑reactive logic inside Effects so you can read the latest props/state without turning them into dependencies or causing needless re‑runs. Keep the Effect’s dependency array minimal and stable. ([React][3]) +* **Let lints guide you.** `eslint-plugin-react-hooks` v6+ ships flat-config presets and **compiler‑powered** rules; don’t suppress—refactor. ([React][9]) + +**Example (pattern to prefer):** + +```tsx +// BEFORE: re-runs on theme change, reconnects unnecessarily +useEffect(() => { + const c = connect(roomId) + c.on('connected', () => showToast('Connected!', theme)) + return () => c.disconnect() +}, [roomId, theme]) + +// AFTER: stable effect with an Effect Event +import { useEffectEvent } from 'react' +const onConnected = useEffectEvent(() => showToast('Connected!', theme)) +useEffect(() => { + const c = connect(roomId) + c.on('connected', onConnected) + return () => c.disconnect() +}, [roomId]) +``` + +--- + +## Lint Rules → Modern Fixes (cheat‑sheet) + +* **`react-hooks/exhaustive-deps`** + + * If the Effect only derives state → **remove the Effect** and compute during render. + * If listening to an external system and you need fresh props/state → wrap non‑reactive logic in **`useEffectEvent`**. ([React][5]) + +* **`@next/next/no-img-element`** → replace `` with `` from `next/image`. ([Next.js][1]) + +* **`@next/next/no-html-link-for-pages`** → use `` for internal navigation. ([Next.js][1]) + +* **Data fetching in client Effects** → move reads to **Server Components**; mutations go through **Server Functions / Server Actions** (`'use server'`). ([Next.js][4]) + +* **Request boundary logic** touching legacy `middleware.ts` → **rename to `proxy.ts`** and export `proxy`. ([Next.js][6]) + +--- + +## Next.js Directory Structure + +All production routes live under the App Router (`app/`). Add new routes there. + +Routes in `app/` should export `generateMetadata` using our helper: + +```ts +import { getAppMetadata } from "@/components/providers/metadata"; +import type { Metadata } from "next"; + +export async function generateMetadata(): Promise { + return getAppMetadata({ title: "My Page" }); +} +``` + +If you add or modify `proxy.ts`, keep it at the root (or `src/`) alongside `app/`/`pages/` and export `proxy`. ([Next.js][10]) + +--- + +## Coding Conventions + +* TypeScript + React functional components with hooks. +* Follow existing code style and naming conventions. +* Maintain clean code standards (measured by SonarQube). +* Tests live in `__tests__/` or `ComponentName.test.tsx`. +* Mock external dependencies and APIs in tests. +* When parsing Seize URLs (or similar), **do not** fall back to placeholder origins; fail fast if base origin is unavailable. + +--- + +## Commit Guidelines + +* Use **Conventional Commits** (`feat:`, `fix:`, etc.). +* **Do not squash**; keep a clear history. +* One logical change per commit. +* **DCO required** on every commit: + + ```text + Signed-off-by: Your Full Name + ``` + +--- + +### Why the policy + +* **Next DevTools MCP** gives agents live, app‑specific context (routes, errors, actions) for accurate fixes. ([Next.js][2]) +* **React 19.2 + Hooks v6** align lints with modern patterns, including `useEffectEvent` and compiler‑powered guidance. ([React][3]) +* **Next 16** introduces explicit caching (`"use cache"`), a clearer network boundary (`proxy.ts`), and stable React Compiler support, so “lint fixes” often become meaningful improvements. ([Next.js][6]) + + +[1]: https://nextjs.org/docs/app/api-reference/config/eslint "Configuration: ESLint | Next.js" +[2]: https://nextjs.org/docs/app/guides/mcp "Guides: Next.js MCP Server | Next.js" +[3]: https://react.dev/reference/react/experimental_useEffectEvent "useEffectEvent – React" +[4]: https://nextjs.org/docs/app/getting-started/server-and-client-components?utm_source=chatgpt.com "Getting Started: Server and Client Components" +[5]: https://react.dev/learn/you-might-not-need-an-effect "You Might Not Need an Effect – React" +[6]: https://nextjs.org/blog/next-16?utm_source=chatgpt.com "Next.js 16" +[7]: https://nextjs.org/docs/app/api-reference/directives/use-cache?utm_source=chatgpt.com "Directives: use cache" +[8]: https://nextjs.org/docs/app/guides/upgrading/version-16?utm_source=chatgpt.com "Upgrading: Version 16" +[9]: https://react.dev/blog/2025/10/01/react-19-2?utm_source=chatgpt.com "React 19.2" +[10]: https://nextjs.org/docs/app/getting-started/proxy?utm_source=chatgpt.com "Getting Started: Proxy" diff --git a/__tests__/AGENTS.md b/__tests__/AGENTS.md index 448c2e134b..70db432c6d 100644 --- a/__tests__/AGENTS.md +++ b/__tests__/AGENTS.md @@ -1,66 +1,134 @@ -# Test Guidelines for Codex +# Codex Testing Guidelines -## Purpose and Structure +## Purpose & Structure -The `__tests__` directory contains Jest test suites for this Next.js project. Tests mirror the source folders such as `components`, `contexts`, `hooks` and `utils` to keep structure familiar. Integration tests for API routes live under `app/api`. Fixtures and helpers used across tests reside in their respective subfolders. +* All tests live in `__tests__`, mirroring source folders (`components`, `contexts`, `hooks`, `utils`). +* API integration tests: `app/api`. +* Shared fixtures & helpers: relevant subfolders. +* Jest automatically picks up mocks from `__mocks__`. -## Testing Frameworks and Tools +## Tools -- **Jest** with the `ts-jest` preset for TypeScript support. -- **@testing-library/react** and **@testing-library/user-event** for React component tests. -- Additional mocks live in `__mocks__` and are automatically picked up by Jest. +* **Jest** + `ts-jest` (TypeScript). +* **@testing-library/react** + **user-event** (React tests). +* Coverage reports in `coverage/`. -## Test Writing Guidelines +## Writing Tests -- Prioritise meaningful coverage that validates business requirements over achieving raw coverage numbers. -- Use the **Arrange – Act – Assert** pattern and keep assertions focused on behaviour, not implementation details. -- Give each test a clear, descriptive name that conveys the scenario and expected outcome. -- Tests should remain independent, deterministic and fast. +* Focus on business value, not raw coverage. +* Follow **Arrange – Act – Assert**. +* One behaviour per test; clear, descriptive names. +* Keep tests independent, deterministic, and fast. +* Use realistic data. -## Test Categorisation and Prioritisation +### Test Types -Consider the following categories when writing tests: +* **Happy Path** – expected workflows. +* **Errors** – invalid input, unexpected scenarios. +* **Edge Cases** – boundaries, rare conditions. +* **Integration** – components & API interactions. +* **Performance/Security** – when relevant. -- **Happy Path Tests** – standard workflows with valid inputs. -- **Error Handling Tests** – behaviour with invalid inputs or unexpected scenarios. -- **Edge Case Tests** – boundary conditions and uncommon situations. -- **Integration Tests** – interactions between components or with API routes. -- **Performance & Security Tests** – only when relevant. +Prioritise high-risk areas first when time-boxed. -When time‑boxed, focus on high‑risk areas first, then fill coverage gaps and polish. +## Time-Boxed Cycle (20 min) -## Time‑Boxed Testing Approach +1. **5–7 min**: core flows. +2. **5–7 min**: edge cases & branches. +3. **5–6 min**: clean up & refine. -A suggested 20‑minute cycle: +## Quality Checklist -1. **Initial Phase (5‑7 min)** – target high‑impact paths and core functionality. -2. **Middle Phase (5‑7 min)** – add tests for edge cases or missing branches. -3. **Final Phase (5‑6 min)** – clean up, improve readability and verify results. +* [ ] Clear, descriptive names +* [ ] Arrange – Act – Assert used +* [ ] Independent & fast +* [ ] One behaviour per test +* [ ] Production-like data -## Test Quality Checklist +## Running Tests -- [ ] Clear, descriptive test names -- [ ] Proper Arrange – Act – Assert structure -- [ ] Independent and deterministic -- [ ] Execute quickly and focus on one behaviour -- [ ] Use realistic, production‑like data +```bash +npm run test:cov:changed # changed files only +npm run test # full suite +npm run lint +npm run type-check +``` -## Execution Instructions +--- -Run tests with: +# Coding Standards -```bash -npm run test:cov:changed +### Complexity + +* Functions ≤ 15 cognitive complexity. +* Extract deep ternaries (>3 levels). +* Break down complex logic. + +### Modern Patterns + +**Iteration** + +```ts +// ❌ Avoid +items.forEach(item => processItem(item)); + +// ✅ Prefer +for (const item of items) { + processItem(item); +} ``` -This command runs Jest only on files changed since `main`. Use `npm run test` if -you need to execute the entire suite. +* Allows `break/continue`. +* Works with async/await. -This command also checks coverage for modified files. Linting and type‑checking should pass as well: +**Array Access** -```bash -npm run lint -npm run type-check +```ts +// ✅ Prefer +const last = array.at(-1); +const secondLast = array.at(-2); ``` -Coverage reports are generated in the `coverage` directory. A summary is printed in the terminal after tests complete. +**Strings** + +```ts +// ✅ Prefer +str.replaceAll('old', 'new'); +``` + +**Globals** + +```ts +// ✅ Prefer +globalThis.fetch(url); +``` + +**Imports** + +* One import per module. +* Order: external → internal → types. +* No duplicates. + +**Accessibility** + +* Use semantic HTML (`