import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createSSRApp, defineComponent, h } from 'vue'; import { renderToString } from 'vue/server-renderer'; import { Accordion } from '@42/core/accordion'; import { useC42 } from './useC42'; /** * SSR-safety: `useC42` creates the controller inside `onMounted`, which Vue * never invokes during server rendering. Rendering with `vue/server-renderer` * must: * 1. not throw, * 2. never construct the controller (no DOM access), and * 3. emit static markup without the controller's runtime DOM mutations * (ARIA attributes, `data-state`, wrapped panel content). */ const SsrHarness = defineComponent({ name: 'SsrHarness', setup() { const el = useC42(Accordion, () => ({})); return () => h('div', { ref: el, 'data-c42-accordion': '' }, [ h('div', { 'data-c42-accordion-item': '', 'data-value': 'a' }, [ h('button', { 'data-c42-accordion-trigger': '' }, 'A'), h('div', { 'data-c42-accordion-panel': '' }, 'Panel A'), ]), ]); }, }); describe('vue/server-renderer SSR safety', () => { beforeEach(() => { document.body.innerHTML = ''; }); afterEach(() => { vi.restoreAllMocks(); }); it('renders to a string without constructing the controller', async () => { const destroySpy = vi.spyOn(Accordion.prototype, 'destroy'); let html = ''; await expect( (async () => { html = await renderToString(createSSRApp(SsrHarness)); })(), ).resolves.toBeUndefined(); // Static markup is present... expect(html).toContain('data-c42-accordion'); expect(html).toContain('data-c42-accordion-trigger'); // ...without any of the controller's runtime DOM mutations. expect(html).not.toContain('aria-expanded'); expect(html).not.toContain('data-state'); expect(html).not.toContain('data-c42-accordion-panel-content'); // onMounted never ran on the server, so no live instance exists. expect(destroySpy).not.toHaveBeenCalled(); }); it('does not mount anything into the document during server render', async () => { expect(document.body.childElementCount).toBe(0); await renderToString(createSSRApp(SsrHarness)); expect(document.body.childElementCount).toBe(0); }); });