import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { createRoot } from '../lib/vdom.ts' import { renderToString } from '../lib/stream.ts' import { invariant } from '../lib/invariant.ts' describe('hydration', () => { let container: HTMLDivElement beforeEach(() => { container = document.createElement('div') document.body.appendChild(container) }) afterEach(() => { document.body.innerHTML = '' }) describe('text node handling', () => { it('adopts single server text node when client has multiple text children', async () => { // Server renders "Hello world" as single text node let html = await renderToString(Hello world) container.innerHTML = html let existingSpan = container.querySelector('span') invariant(existingSpan) let originalTextNode = existingSpan.firstChild invariant(originalTextNode instanceof Text) // Client has two text children: ["Hello ", "world"] let root = createRoot(container) root.render( {'Hello '} {'world'} , ) root.flush() // Span should be adopted expect(container.querySelector('span')).toBe(existingSpan) // Text content should match (even if internal structure differs) expect(existingSpan.textContent).toBe('Hello world') }) it('subsequent update patches consolidated text content', async () => { let html = await renderToString(Hello world) container.innerHTML = html let existingSpan = container.querySelector('span') invariant(existingSpan) let name = 'world' function render() { root.render( {'Hello '} {name} , ) root.flush() } let root = createRoot(container) render() expect(existingSpan.textContent).toBe('Hello world') // Update the dynamic part name = 'Ryan' render() expect(existingSpan.textContent).toBe('Hello Ryan') }) it('handles null children as empty text', async () => { let html = await renderToString(
{null}
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let root = createRoot(container) root.render(
{null}
) root.flush() expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.textContent).toBe('') }) it('handles undefined children as empty text', async () => { let html = await renderToString(
{undefined}
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let root = createRoot(container) root.render(
{undefined}
) root.flush() expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.textContent).toBe('') }) it('handles false children as empty text', async () => { let html = await renderToString(
{false}
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let root = createRoot(container) root.render(
{false}
) root.flush() expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.textContent).toBe('') }) it('handles true children as empty text', async () => { let html = await renderToString(
{true}
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let root = createRoot(container) root.render(
{true}
) root.flush() expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.textContent).toBe('') }) }) })