import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { link } from '../index.ts' 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 = '' for (let node of Array.from(document.head.childNodes)) { document.head.removeChild(node) } }) describe('special case props to HTML attributes', () => { it('hydrates className as class attribute', async () => { let html = await renderToString(
Hello
) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) expect(existingDiv.getAttribute('class')).toBe('my-class') let root = createRoot(container) root.render(
Hello
) root.flush() // Same DOM node should be adopted expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.getAttribute('class')).toBe('my-class') }) it('hydrates htmlFor as for attribute', async () => { let html = await renderToString(
, ) container.innerHTML = html let existingLabel = container.querySelector('label') invariant(existingLabel) expect(existingLabel.getAttribute('for')).toBe('my-input') let root = createRoot(container) root.render(
, ) root.flush() expect(container.querySelector('label')).toBe(existingLabel) expect(existingLabel.getAttribute('for')).toBe('my-input') }) it('hydrates tabIndex as tabindex attribute', async () => { let html = await renderToString() container.innerHTML = html let existingButton = container.querySelector('button') invariant(existingButton) let root = createRoot(container) root.render() root.flush() expect(container.querySelector('button')).toBe(existingButton) expect(existingButton.getAttribute('tabindex')).toBe('0') }) it('hydrates role and tabIndex added by link mixins', async () => { let html = await renderToString(
  • Docs
  • ) container.innerHTML = html let existingItem = container.querySelector('li') invariant(existingItem) let root = createRoot(container) root.render(
  • Docs
  • ) root.flush() expect(container.querySelector('li')).toBe(existingItem) expect(existingItem.getAttribute('role')).toBe('link') expect(existingItem.getAttribute('tabindex')).toBe('0') }) it('hydrates acceptCharset as accept-charset attribute', async () => { let html = await renderToString(
    ) container.innerHTML = html let existingForm = container.querySelector('form') invariant(existingForm) let root = createRoot(container) root.render() root.flush() expect(container.querySelector('form')).toBe(existingForm) expect(existingForm.getAttribute('accept-charset')).toBe('UTF-8') }) it('hydrates httpEquiv as http-equiv attribute', async () => { let html = await renderToString() container.innerHTML = html let existingMeta = container.querySelector('meta') invariant(existingMeta) let root = createRoot(container) root.render() root.flush() expect(container.querySelector('meta')).toBe(existingMeta) expect(document.head.querySelector('meta')).toBeNull() expect(existingMeta.getAttribute('http-equiv')).toBe('refresh') }) it('hydrates aria-* attributes unchanged', async () => { let html = await renderToString( , ) container.innerHTML = html let existingButton = container.querySelector('button') invariant(existingButton) let root = createRoot(container) root.render( , ) root.flush() expect(container.querySelector('button')).toBe(existingButton) expect(existingButton.getAttribute('aria-label')).toBe('Close') expect(existingButton.getAttribute('aria-expanded')).toBe('false') }) it('hydrates data-* attributes unchanged', async () => { let html = await renderToString(
    ) container.innerHTML = html let existingDiv = container.querySelector('div') invariant(existingDiv) let root = createRoot(container) root.render(
    ) root.flush() expect(container.querySelector('div')).toBe(existingDiv) expect(existingDiv.getAttribute('data-testid')).toBe('my-div') expect(existingDiv.getAttribute('data-value')).toBe('42') }) it('hydrates SVG xlinkHref as xlink:href', async () => { let html = await renderToString( , ) container.innerHTML = html let existingUse = container.querySelector('use') invariant(existingUse) let root = createRoot(container) root.render( , ) root.flush() expect(container.querySelector('use')).toBe(existingUse) expect(existingUse.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('#icon-star') }) it('hydrates SVG viewBox with preserved case', async () => { let html = await renderToString() container.innerHTML = html let existingSvg = container.querySelector('svg') invariant(existingSvg) let root = createRoot(container) root.render() root.flush() expect(container.querySelector('svg')).toBe(existingSvg) expect(existingSvg.getAttribute('viewBox')).toBe('0 0 24 24') }) it('hydrates SVG preserveAspectRatio with preserved case', async () => { let html = await renderToString() container.innerHTML = html let existingSvg = container.querySelector('svg') invariant(existingSvg) let root = createRoot(container) root.render() root.flush() expect(container.querySelector('svg')).toBe(existingSvg) expect(existingSvg.getAttribute('preserveAspectRatio')).toBe('xMidYMid meet') }) it('hydrates SVG filterUnits with canonical case and semantics', async () => { let html = await renderToString( , ) container.innerHTML = html let existingFilter = container.querySelector('#f') invariant(existingFilter instanceof SVGFilterElement) let root = createRoot(container) root.render( , ) root.flush() expect(container.querySelector('#f')).toBe(existingFilter) expect(existingFilter.getAttribute('filterUnits')).toBe('userSpaceOnUse') expect(existingFilter.getAttribute('filter-units')).toBe(null) expect(existingFilter.filterUnits.baseVal).toBe(1) }) }) })