import { describe, it, expect } from 'vitest' import { createRoot } from './vdom.ts' import { invariant } from './invariant.ts' import { renderToString } from './stream.ts' import { dom } from '@remix-run/events' describe('hydration', () => { it('adopts text nodes', async () => { let container = document.createElement('div') container.innerHTML = 'Hello, world!' let node = container.firstChild let root = createRoot(container) root.render('Hello, world!') expect(container.innerHTML).toBe('Hello, world!') expect(container.firstChild).toBe(node) }) it('adopts host nodes', async () => { let container = document.createElement('div') container.innerHTML = '
' let node = container.firstChild let root = createRoot(container) root.render(
) expect(container.innerHTML).toBe('
') expect(container.firstChild).toBe(node) }) it('adopts dom nodes with children', async () => { let container = document.createElement('div') container.innerHTML = '
Hello, world!
' let node = container.firstChild let span = container.querySelector('span') invariant(span) let root = createRoot(container) root.render(
Hello, world!
, ) expect(container.innerHTML).toBe('
Hello, world!
') expect(container.firstChild).toBe(node) expect(container.querySelector('span')).toBe(span) }) it('adopts dom nodes with children text', async () => { let container = document.createElement('div') container.innerHTML = '
Hello, world!
' let node = container.firstChild invariant(node) let text = node.firstChild let root = createRoot(container) root.render(
Hello, world!
) expect(container.innerHTML).toBe('
Hello, world!
') expect(container.firstChild).toBe(node) let newText = container.firstChild?.firstChild invariant(newText) expect(newText).toBe(text) }) it('adopts dom nodes with children text and elements', async () => { let container = document.createElement('div') container.innerHTML = '
Hello, world!
' let div = container.firstChild invariant(div) let text = div.firstChild invariant(text instanceof Text) let span = container.querySelector('span') invariant(span) let root = createRoot(container) root.render(
Hello, world!
, ) expect(container.innerHTML).toBe('
Hello, world!
') expect(container.firstChild).toBe(div) let newText = container.firstChild?.firstChild expect(newText).toBe(text) expect(container.querySelector('span')).toBe(span) }) it('adopts fragments', () => { let container = document.createElement('div') container.innerHTML = '

Hello

world!

' let ps = container.querySelectorAll('p') expect(ps).toHaveLength(2) let root = createRoot(container) root.render( <>

Hello

world!

, ) expect(container.innerHTML).toBe('

Hello

world!

') let newPs = container.querySelectorAll('p') invariant(newPs.length === 2) expect(newPs[0]).toEqual(ps[0]) expect(newPs[1]).toEqual(ps[1]) }) it('adopts nodes within components', () => { let container = document.createElement('div') container.innerHTML = '
Hello, world!
' let div = container.firstChild invariant(div) let text = div.firstChild invariant(text instanceof Text) function App() { return
Hello, world!
} let root = createRoot(container) root.render() expect(container.innerHTML).toBe('
Hello, world!
') expect(container.firstChild).toBe(div) expect(container.firstChild?.firstChild).toBe(text) }) it('adopts mixed nodes within components', () => { let container = document.createElement('div') container.innerHTML = '
Hello, world!
' let div = container.firstChild invariant(div) let text = div.firstChild invariant(text instanceof Text) let span = container.querySelector('span') invariant(span) function App() { return (
Hello, world!
) } let root = createRoot(container) root.render() expect(container.innerHTML).toBe('
Hello, world!
') expect(container.firstChild).toBe(div) expect(container.firstChild?.firstChild).toBe(text) expect(container.querySelector('span')).toBe(span) }) it('ignores comments', () => { let container = document.createElement('div') container.innerHTML = '
Hello
' let div = container.firstChild invariant(div) let span = container.querySelector('span') invariant(span) let text = span.firstChild invariant(text) let root = createRoot(container) root.render(
Hello
, ) expect(container.innerHTML).toBe('
Hello
') expect(container.querySelector('div')).toBe(div) expect(container.querySelector('span')).toBe(span) expect(span.firstChild).toBe(text) }) it('corrects text mismatches', () => { let container = document.createElement('div') container.innerHTML = 'Hello' let text = container.firstChild let root = createRoot(container) root.render('Hello, world!') expect(container.innerHTML).toBe('Hello, world!') expect(container.firstChild).toBe(text) }) it.todo('corrects sequential text node mismatches') it('corrects attribute mismatches', () => { let container = document.createElement('div') container.innerHTML = '
' let div = container.firstChild invariant(div) let root = createRoot(container) root.render(
) expect(container.innerHTML).toBe('
') expect(container.firstChild).toBe(div) }) it('attaches events', () => { let container = document.createElement('div') container.innerHTML = '' let button = container.firstChild invariant(button instanceof HTMLButtonElement) let root = createRoot(container) let clicked = false root.render( , ) root.flush() expect(container.querySelector('button')).toBe(button) button.click() expect(clicked).toBe(true) }) it('ignores excess nodes', () => { let container = document.createElement('div') container.innerHTML = '
Who do you think you are?
' let root = createRoot(container) root.render(
Who do you think you are?
, ) expect(container.innerHTML).toBe( '
Who do you think you are?
', ) root.render(
Leave it be
, ) expect(container.innerHTML).toBe('
Leave it be
') }) it('patches up mismatched elements', () => { let container = document.createElement('div') container.innerHTML = '
Who do you think you are?
' let root = createRoot(container) root.render(
I am!
) expect(container.innerHTML).toBe('
I am!
') }) it('works with renderToString', async () => { function App() { return (
Content
Footer
) } let html = await renderToString() let container = document.createElement('div') container.innerHTML = html let main = container.querySelector('main') invariant(main) let root = createRoot(container) root.render() expect(container.innerHTML).toBe(html) expect(container.querySelector('main')).toBe(main) }) it.skip('collapses text nodes', () => { let container = document.createElement('div') container.innerHTML = 'All Text' let root = createRoot(container) let value = 'Text' root.render(Some {value}) expect(container.innerHTML).toBe('All Text') }) })