// @vitest-environment node import { describe, expect, it } from 'vitest'; import { renderToStaticMarkup } from 'react-dom/server'; import { cases } from './cases'; /** * Every component, rendered on a server. * * The kit's whole consumption story is Next.js App Router: `'use client'` sits * on top of every component file and `scripts/check-exports.mjs` fails the build * if the directive does not survive bundling. But that check is **textual** — it * proves the string is present in `dist`, not that anything works. A `'use * client'` component is still server-rendered on the first request; the * directive only marks where hydration begins. * * So until this file existed, nothing in the kit had ever been rendered without * a DOM. The failure it catches is the one a component library ships most * often and notices last: a module that touches `document`, `window`, * `matchMedia` or `localStorage` at import time or during the first render. In * a consumer's app that is not a subtle bug — it is a 500 on the page. * * ── Why `@vitest-environment node`, and why it is the whole point ─────────── * The rest of the suite runs in jsdom, where `document` exists and this test * would pass no matter what the components did. The pragma on line 1 is what * gives the file its teeth. Do not "unify" it with the other suites. * * The table is shared with `a11y.test.tsx` (see `cases.tsx`) so a new component * cannot land in one gate and miss the other. */ describe('server rendering', () => { it.each(cases)('%s renders without a DOM', (_name, element) => { /* Asserting on the markup rather than just "it did not throw": a component that swallows its own error and returns null would otherwise pass. Every case in the table is composed to render visible content. */ const html = renderToStaticMarkup(element); expect(html).toBeTruthy(); expect(html.length).toBeGreaterThan(0); }); it('has no DOM globals in scope', () => { /* Guards the guard. If a setup file or a future config change ever leaks a jsdom environment into this file, every assertion above would keep passing while proving nothing — the same silent-pass shape the kit has been bitten by before. */ expect(typeof document).toBe('undefined'); expect(typeof window).toBe('undefined'); }); });