# Gate Tests > Cómo usar las directivas de test @gate / @force-gate en lugar de it.skip o patrones de skip falso-verde, con las condiciones, el patrón de axis y los comandos de verificación. Fuente: https://skillsagentes.com/skills/vercel/next.js/gate-tests Markdown: https://skillsagentes.com/skills/vercel/next.js/gate-tests.md Repositorio: https://github.com/vercel/next.js Autor: vercel Licencia: MIT Actualizado: hace 4 días Coste de contexto: 140 tok instalada, 2.7k tok al activarse, 2.7k tok con todos los archivos del bundle Bundle: 1 archivo, 10 KB Permisos que pide: ninguno declarado ## Instalación Un skill son archivos markdown: los mismos archivos valen para cualquier agente y lo único que cambia es el directorio de destino, es decir la bandera `--agent`. Añade `-g` para instalarlo en todos los proyectos de la máquina. ```bash # Claude Code npx -y skills add vercel/next.js --skill gate-tests --agent claude-code # Cursor npx -y skills add vercel/next.js --skill gate-tests --agent cursor # Codex npx -y skills add vercel/next.js --skill gate-tests --agent codex # Gemini CLI npx -y skills add vercel/next.js --skill gate-tests --agent gemini # Windsurf npx -y skills add vercel/next.js --skill gate-tests --agent windsurf # Cline npx -y skills add vercel/next.js --skill gate-tests --agent cline ``` ## Qué hace - Sustituye it.skip y patrones de skip falso-verde por directivas @gate o @force-gate - Explica cómo elegir entre @gate, @force-gate estático y @force-gate perezoso según el caso - Describe el patrón de fixture con __NEXT_TEST_AXIS para cubrir ambos estados de un flag experimental - Detalla los comandos para verificar una suite gateada en cada estado (plano, axis, dev) ## Cuándo usarla - Un test falla de forma conocida bajo alguna dimensión de la matriz (modo dev, un bundler, un flag experimental como cacheComponents) - Se está convirtiendo un guard if (isNextDev) return o un describe.skip basado en una variable de entorno - Se añade una condición nueva a test/lib/gate/conditions.ts - Se necesita mapear el flag experimental de un fixture a una letra de __NEXT_TEST_AXIS ## Qué la activa - "Convierte este it.skip en un @gate en lugar de saltarlo" - "¿Cómo reemplazo este if (isNextDev) return por un @force-gate?" - "Necesito que este test cubra tanto el estado on como off de cacheComponents" - "Ayúdame a verificar esta suite gateada en modo dev y en el axis A" ## Antes de instalar - Cada nombre usado en una pragma debe estar declarado en test/lib/gate/conditions.ts o la suite falla en la recolección. ## Archivos - SKILL.md — 10 KB ## SKILL.md Reproducido tal cual desde vercel/next.js bajo MIT. Esta sección es el documento original y está en inglés. # Gating tests with `@gate` / `@force-gate` Full reference: [`test/lib/gate/README.md`](../../../test/lib/gate/README.md). This skill is the decision guide: which directive to reach for, the standard conversion patterns, and how to verify. ## Never write these — gate instead | Anti-pattern | Replacement | | ---------------------------------------------------------------------- | --------------------------------------------------------------------------- | | `it.skip('...')` for a known failure | `// @gate ` (or `@gate FIXME` if no condition explains it) | | `if (isNextDev) { test('skipped in dev mode', () => {}); return }` | `// @force-gate prefetching` (or `!dev`) on the `describe` | | `(flagEnabled ? describe.skip : describe)(...)` keyed on `process.env` | `// @force-gate ` (lazy) on the `describe` | | Duplicating a fixture directory per flag state | one fixture keyed on `__NEXT_TEST_AXIS` + a `@gate`/`@force-gate` | | Branching expectations on `process.env.__NEXT_CACHE_COMPONENTS` | `if (await gate((c) => c.cacheComponents))` (`gate` from `next-test-utils`) | The skip patterns are fake-greens: nothing tells you when the bug they hide is fixed. `@gate` still runs the body and fails the suite the day the "known failure" starts passing, so stale workarounds get deleted instead of rotting. ## Choosing the directive Ask what kind of difference you're encoding: 1. **A behavior change — both states assert something meaningful.** Don't gate the test at all: fork inside the body with the runtime `gate()` — same condition registry, no inversion — which pinpoints exactly what differs, and also covers `it.each`, where a pragma cannot attach: `if (await gate((c) => c.cacheComponents)) { ... } else { ... }`. It mirrors React's `gate(flags => ...)`; a pragma expression string works too (`await gate('cacheComponents && !dev')`). A suite-level pragma is too coarse here — it hides _what_ is different between the states. 2. **A flag that changes the behavior of existing surface** (`cacheComponents`, `optimisticRouting`) **and the suite is written for one state.** `// @gate ` on the test or `describe`. The body runs; a false condition inverts the expectation (failure absorbed, a pass fails as "stale gate"). The off state fails for a meaningful reason — the behavior differs — so a pass is real information: the gate is stale, delete it. 3. **A new API — the off state proves nothing.** Typically `// @force-gate ` (lazy) on the `describe`. An API that throws when its flag is off — or is inert, like `useOffline()`, which compiles to a hook that always reports online — can only fail vacuously (often slowly, by timing out), and browser e2e time is considerable, so skip the run (and the fixture build) instead of paying for it. Working example: `test/e2e/app-dir/use-offline/`. This is discretion, not a rule: when the flag changes behavior the suite can observe, the off state is meaningful and `@gate` buys the staleness check. 4. **`@force-gate ` also when running the body is impossible**, not merely failing: prefetching is off in dev, deploy has no local build output, the fixture cannot even build under the condition. - Static condition (`!dev`, `bundler`…) → real Jest `○ skipped` at collection. - Lazy condition on a `describe` → the fixture **build is skipped** when false; tests report passed-with-`⚠ skipped by @force-gate` (Jest cannot skip at runtime). Build-skipping covers `start`/`dev` suites where `nextTestSetup` owns the build — not `skipStart` suites, not deploy. 5. Pragmas stack: a common pair is a static `// @force-gate prefetching` plus a lazy `// @gate ` on the same `describe`. ### Is the off-state run worth its cost? Browser e2e time is not free, so weigh what the gated-off run buys. For a behavior flag it usually replaces a run that was already being paid for — a fixture that pins its flags runs identically with and without the axis set, so keying the flag on an axis converts a redundant duplicate into coverage — and it is what proves a pass isn't vacuous: a test that passes with the feature off wasn't testing the feature. Absorbed failures also fail fast, so the off-state run is cheaper than it sounds. For a new API the calculus flips: the off state can only throw, which proves nothing, so use a lazy `// @force-gate ` on the `describe` — the fixture build is skipped too, so the off state costs almost nothing. ## Conditions Every name in a pragma must be declared in `test/lib/gate/conditions.ts` (typos fail the suite at collection). Two tiers: - **static** — the run's shape: `dev`, `start`, `deploy`, `mode`, `turbopack`, `rspack`, `webpack`, `bundler`, `react18`, `wasm`, `ci`, plus the always-false `FIXME`/`TODO`. `prod` and `prefetching` are semantic aliases for `!dev` — prefer the name that states _why_ the suite cannot run. - **lazy** — a predicate over the fixture's _resolved_ `next.config` (`cacheComponents`, `ppr`, `useOffline`, `output`, …). Adding one is a two-line change; follow the guidance at the top of `conditions.ts`. The rule that matters: **lazy conditions read the resolved config, never `process.env`** — env vars don't survive config resolution (`__NEXT_CACHE_COMPONENTS` only applies when the fixture doesn't set `cacheComponents` itself, and resolution implies flags the fixture never mentions). ## Pattern: cover both states of an experimental flag Instead of pinning a flag on (which makes the plain and axis runs identical), key it on a test axis and gate the suite. Axes are lettered (`A`, `B`, …) — a fixed enumeration, not a boolean and not a sharding bucket. Key the flag so it is **enabled by default** — then the suite exercises the feature in plain local runs with no special env, and the axis run covers the off state: ```js // next.config.js — pin every dimension except the one under test const nextConfig = { cacheComponents: true, experimental: { concurrentRouterQueue: process.env.__NEXT_TEST_AXIS !== 'A', }, } ``` ```ts // @gate concurrentRouterQueue it('fails loudly on link navigation', async () => { ... }) ``` The plain run exercises the feature; the axis-A run covers the off state — the gated tests are expected to fail there, and the suite fails the day they start passing. Working example: `test/e2e/app-dir/concurrent-router-queue/` (tests whose expectations hold in both states stay ungated). The same keying pairs with a lazy `@force-gate` when the off state proves nothing — `test/e2e/app-dir/use-offline/` — which skips the redundant axis run (build included) instead of covering it. Axis `A` aliases `__NEXT_CACHE_COMPONENTS` for now (see `scripts/run-jest.sh`) — fine, because these fixtures pin `cacheComponents` explicitly, so that run's env default is a no-op for them. **Keep exactly one flag varying per fixture.** A red shard must attribute to a single dimension. ## Pitfalls - A pragma the transform can't attach is a **hard error**: a blank line between pragma and `it(`, `it.each`/`it.failing`, or a pragma inside a JSDoc block. Prose comments must not begin with `@gate`. A pragma on a skipped test (`it.skip`, `xit`, …) errors as ambiguous — remove the skip or the pragma. A skip without a pragma is respected. - A `describe`-level gate does not reach `it.each` tests. - Gated-false bodies that fail by _stalling_ waste the full Jest timeout — and under a lazy gate they fail the suite anyway (the runtime inversion only absorbs thrown errors; a static gate rides Jest's native `test.failing`, which does absorb timeouts). Bodies that fail via `retry()` timeouts also make the off-state run slow; a fast first assertion is worth having. - Failures cascade in the off state: an absorbed failure mid-body skips the body's cleanup (e.g. a browser context left offline), so later tests may fail for cascade reasons. Acceptable for a tripwire, but don't puzzle over the individual failure messages in a gated-off run. - `afterEach` failures (e.g. redbox matchers) are not gated, only the body is. Hooks under a false lazy `@force-gate` are the exception: they are skipped with the suite instead of running against a fixture that was never booted. - `jest.retryTimes(1)` on non-dev CI means a _flaky_ gated-false test passes whenever it happens to fail; the tripwire is only deterministic for deterministic tests. - Gated titles are unchanged in the Jest output; the `⚠ gated test failed as expected` log line is the only signal. - `pragma-transform.js` bails out early on files containing neither `@gate` nor `@force-gate` as substrings — keep both checks if you touch it. ## Verify a gated suite in every state it can run in ```sh # plain run (flag on): expect normal passes, no warnings NEXT_SKIP_ISOLATE=1 pnpm test-start-webpack test/e2e/app-dir//.test.ts # axis run (flag off): expect `⚠ gated test failed as expected (@gate …)` __NEXT_TEST_AXIS=A NEXT_SKIP_ISOLATE=1 pnpm test-start-webpack test/e2e/app-dir//.test.ts # dev (static @force-gate !dev): expect `○ skipped` at collection, no fixture boot NEXT_SKIP_ISOLATE=1 pnpm test-dev-webpack test/e2e/app-dir//.test.ts ``` A suite with a lazy `@force-gate` on the `describe` should additionally show `skipping build` behavior (no `next build`) in the state where the condition is false. Unit tests for the infrastructure itself: `pnpm test-unit test/unit/gate/`. ## Related skills - `$flags` — adding the experimental flag itself (config-shared, schema, define-env) - `$router-act` — the prefetch-timing patterns most gated suites also use ## Dónde encaja - Categoría: [Testing y QA](https://skillsagentes.com/categorias/testing-qa.md) — Flujos de testing unitario, de integración y end-to-end. - Creador: [vercel](https://skillsagentes.com/creators/vercel.md) — 81 skills en el directorio - [Todas las skills](https://skillsagentes.com/skills.md) - [Ranking de instalaciones](https://skillsagentes.com/ranking.md) ## Otras skills del mismo repositorio - [Next Cache Components Adoption](https://skillsagentes.com/skills/vercel/next.js/next-cache-components-adoption.md): Activa Cache Components en una app de Next.js y resuelve las rutas bloqueantes que surgen: el flag cacheComponents, errores de blocking-prerender/instant validation y el codemod cache-components-instant-false. - [Next Cache Components Optimizer](https://skillsagentes.com/skills/vercel/next.js/next-cache-components-optimizer.md): Lleva una ruta de Next.js a navegación instantánea bajo Cache Components o PPR mediante un bucle agéntico: codifica el objetivo como un e2e instant() en rojo y lo trabaja hasta verde, ruta a ruta. - [Next Partial Prefetching Adoption](https://skillsagentes.com/skills/vercel/next.js/next-partial-prefetching-adoption.md): Activa Partial Prefetching en una app Next.js y trabaja los insights que surge: flag `partialPrefetching`, `export const prefetch = 'partial'`, auditoría de `Link prefetch={true}` y tests `instant()`. - [Next Dev Loop](https://skillsagentes.com/skills/vercel/next.js/next-dev-loop.md): Verifica el comportamiento en runtime de Next.js tras editar código de la aplicación. Combina /_next/mcp, la visión de Next.js, con agent-browser, la del navegador. Requiere un next dev en marcha. - [Deploy Release Test](https://skillsagentes.com/skills/vercel/next.js/deploy-release-test.md): Valida el paquete de preview de Next.js específico de un commit y dispara manualmente toda la suite de tests de despliegue vía el workflow test_e2e_deploy_release.yml de GitHub Actions. ## Skills relacionadas - [Next Dev Loop](https://skillsagentes.com/skills/vercel/next.js/next-dev-loop.md): Verifica el comportamiento en runtime de Next.js tras editar código de la aplicación. Combina /_next/mcp, la visión de Next.js, con agent-browser, la del navegador. Requiere un next dev en marcha. - [Pr Status Triage](https://skillsagentes.com/skills/vercel/next.js/pr-status-triage.md): Tría fallos de CI y comentarios de revisión de PR con scripts/pr-status.js: prioriza por bloqueo (build, lint, tipos, tests), empareja variables de CI para reproducir en local y distingue flakies. - [Router Act](https://skillsagentes.com/skills/vercel/next.js/router-act.md): Cómo escribir tests end-to-end con createRouterAct y LinkAccordion. Úsalo al escribir tests que controlan el momento de las peticiones internas de Next.js, como los prefetches. - [Sandbox Bench](https://skillsagentes.com/skills/vercel/next.js/sandbox-bench.md): Compara el rendimiento de cambios de React o Next.js en VMs de Vercel Sandbox con estadística A/B pareada: rps, latencia, p95, TTFB, RSS y bytes de documento y Flight. - [Authoring Skills](https://skillsagentes.com/skills/vercel/next.js/authoring-skills.md): Cómo crear y mantener skills de agente en .agents/skills/. Úsalo al crear un SKILL.md, escribir descripciones, elegir campos de frontmatter o decidir qué va en un skill y qué en AGENTS.md. --- Skills Agentes · [Índice de páginas en markdown](https://skillsagentes.com/sitemap.md) · [Inicio](https://skillsagentes.com/index.md)