import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { renderToString } from 'react-dom/server';
import { Accordion as AccordionController } from '@42/core/accordion';
import { Accordion } from './components/Accordion';
/**
* SSR-safety: `useC42` defers all DOM work to `useEffect`, which never runs on
* the server. Rendering an adapter component with `react-dom/server` must:
* 1. not throw,
* 2. never construct the controller (no DOM access), and
* 3. produce static markup without any of the controller's runtime mutations
* (ARIA attributes, `data-state`, wrapped panel content).
*/
describe('react-dom/server SSR safety', () => {
beforeEach(() => {
// Reset jsdom DOM so we can assert the render touched nothing.
document.body.innerHTML = '';
});
afterEach(() => {
vi.restoreAllMocks();
});
it('does not construct the controller during server render', () => {
const ctor = vi.spyOn(AccordionController.prototype, 'destroy');
let html = '';
expect(() => {
html = renderToString(
,
);
}).not.toThrow();
// Static markup is present...
expect(html).toContain('data-c42-accordion');
expect(html).toContain('data-c42-accordion-trigger');
// ...but none of the controller's runtime DOM mutations are.
expect(html).not.toContain('aria-expanded');
expect(html).not.toContain('data-state');
expect(html).not.toContain('data-c42-accordion-panel-content');
// The controller's lifecycle never ran (destroy belongs to a live instance).
expect(ctor).not.toHaveBeenCalled();
});
it('does not touch the document while rendering on the server', () => {
expect(document.body.childElementCount).toBe(0);
renderToString(
,
);
// renderToString returns a string; it must not mount anything into the DOM.
expect(document.body.childElementCount).toBe(0);
});
});