From 45de7ce15284646ab9acae9ae8dd58dabd2cce5e Mon Sep 17 00:00:00 2001 From: sidgaikwad Date: Fri, 4 Sep 2026 12:14:07 +0530 Subject: [PATCH] test: cover the SSR and 'use client' guarantees The README states the component works in React Server Components environments, and that it touches the DOM only inside effects. Nothing verified either claim: the whole suite runs in jsdom, and the 'use client' banner is a tsup config detail that a bundler or config change could silently drop with every existing test still passing. Add test/ssr.test.tsx, which runs with `@vitest-environment node`: - asserts there really is no DOM in that environment, so the rest of the file proves something - renders the component with renderToString, bare and with every prop set - asserts both built bundles start with 'use client' The dist assertion is skipped when dist/ is absent, so it runs in the CI job that builds first and does not fail the Node/React matrix jobs. --- test/ssr.test.tsx | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/ssr.test.tsx diff --git a/test/ssr.test.tsx b/test/ssr.test.tsx new file mode 100644 index 0000000..19d5f82 --- /dev/null +++ b/test/ssr.test.tsx @@ -0,0 +1,62 @@ +// @vitest-environment node +// +// The rest of the suite runs in jsdom, so nothing here verified the +// README's claim that the component "works out of the box in React Server +// Components environments ... and touches the DOM only inside effects". +// This file runs with no DOM at all. +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { renderToString } from 'react-dom/server'; + +import ImageEditor from '../src'; + +it('has no DOM in this environment', () => { + // Guards the guard: if jsdom leaked in, the test below proves nothing. + expect(typeof document).toBe('undefined'); + expect(typeof window).toBe('undefined'); +}); + +it('renders to a string on the server without touching the DOM', () => { + const html = renderToString( + + ); + + expect(html).toContain('id="ssr"'); +}); + +it('renders on the server with every prop set', () => { + // Effects never run on the server, so a prop that reaches the DOM during + // render rather than in an effect would throw here. + expect(() => + renderToString( + {}} + onSave={() => {}} + onCancel={() => {}} + onLoadError={() => {}} + onError={() => {}} + /> + ) + ).not.toThrow(); +}); + +// The 'use client' banner comes from tsup config, which nothing else +// checks — a bundler or config change could silently drop it and every +// existing test would still pass. Runs after `npm run build`; skipped in +// the React/Node matrix jobs, which do not build. +const dist = (file: string) => resolve(__dirname, '..', 'dist', file); +const built = existsSync(dist('index.mjs')); + +it.skipIf(!built)('ships the "use client" directive in both builds', () => { + for (const file of ['index.js', 'index.mjs']) { + expect(readFileSync(dist(file), 'utf8').startsWith("'use client';")).toBe( + true + ); + } +});