import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { createRoot, resetStyleState } from '../lib/vdom.ts' import { renderToString } from '../lib/stream.ts' import { invariant } from '../lib/invariant.ts' import { css } from '../index.ts' describe('hydration', () => { let container: HTMLDivElement beforeEach(() => { container = document.createElement('div') document.body.appendChild(container) }) afterEach(() => { document.body.innerHTML = '' }) describe('css mixin hydration', () => { afterEach(() => { // Reset the global style manager state between tests resetStyleState() }) it('hydrates element with css mixin and adopts server style', async () => { let html = await renderToString(
Hello
) // Inject server styles into document.head and append body content container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let originalClass = existingDiv.className expect(originalClass).toMatch(/rmxc-/) let root = createRoot(container) root.render(
Hello
) root.flush() // Element should be adopted (same DOM node) expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.className).toBe(originalClass) // Style should apply expect(getComputedStyle(existingDiv).color).toBe('rgb(255, 0, 0)') }) it('updates css mixin after hydration', async () => { let html = await renderToString(
Hello
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let root = createRoot(container) root.render(
Hello
) root.flush() expect(getComputedStyle(existingDiv).color).toBe('rgb(255, 0, 0)') // Update to different css mixin root.render(
Hello
) root.flush() expect(getComputedStyle(existingDiv).color).toBe('rgb(0, 0, 255)') expect(existingDiv.className).toMatch(/rmxc-/) }) it('hydrates css mixin combined with className', async () => { let html = await renderToString(
Hello
, ) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) expect(existingDiv.className).toContain('my-class') expect(existingDiv.className).toMatch(/rmxc-/) let root = createRoot(container) root.render(
Hello
, ) root.flush() // Element should be adopted expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.className).toContain('my-class') expect(existingDiv.className).toMatch(/rmxc-/) expect(getComputedStyle(existingDiv).color).toBe('rgb(0, 128, 0)') }) it('multiple elements with same css mixin share style during hydration', async () => { let html = await renderToString(
First Second
, ) container.innerHTML = html let spans = container.querySelectorAll('span') expect(spans).toHaveLength(2) let firstClassName = spans[0].className let secondClassName = spans[1].className expect(firstClassName).toBe(secondClassName) expect(firstClassName).toMatch(/rmxc-/) let root = createRoot(container) root.render(
First Second
, ) root.flush() // Both spans should be adopted let hydratedSpans = container.querySelectorAll('span') expect(hydratedSpans[0]).toBe(spans[0]) expect(hydratedSpans[1]).toBe(spans[1]) expect(hydratedSpans[0].className).toBe(firstClassName) expect(hydratedSpans[1].className).toBe(firstClassName) // Style should apply to both expect(getComputedStyle(hydratedSpans[0]).color).toBe('rgb(128, 0, 128)') expect(getComputedStyle(hydratedSpans[1]).color).toBe('rgb(128, 0, 128)') }) it('handles element unmount with css mixin after hydration', async () => { let html = await renderToString(
Will unmount
, ) container.innerHTML = html let root = createRoot(container) root.render(
Will unmount
, ) root.flush() expect(container.querySelector('span')).not.toBe(null) // Remove the span root.render(
) root.flush() // Span should be gone expect(container.querySelector('span')).toBe(null) }) it('adds css mixin during hydration when server had none', async () => { let html = await renderToString(
Hello
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) expect(existingDiv.className).toBe('') // Client adds css mixin let root = createRoot(container) root.render(
Hello
) root.flush() // Element should be adopted and css applied expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.className).toMatch(/rmxc-/) expect(getComputedStyle(existingDiv).color).toBe('rgb(0, 255, 255)') }) }) })