|
@@ -1,9 +1,19 @@
|
|
|
// import { type SSRContext, renderToString } from '@vue/server-renderer'
|
|
// import { type SSRContext, renderToString } from '@vue/server-renderer'
|
|
|
-import { createVaporSSRApp, renderEffect, setText, template } from '../src'
|
|
|
|
|
-import { nextTick, ref } from '@vue/runtime-dom'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ child,
|
|
|
|
|
+ createVaporSSRApp,
|
|
|
|
|
+ delegateEvents,
|
|
|
|
|
+ next,
|
|
|
|
|
+ renderEffect,
|
|
|
|
|
+ setClass,
|
|
|
|
|
+ setText,
|
|
|
|
|
+ template,
|
|
|
|
|
+} from '../src'
|
|
|
|
|
+import { nextTick, ref, toDisplayString } from '@vue/runtime-dom'
|
|
|
|
|
|
|
|
function mountWithHydration(html: string, setup: () => any) {
|
|
function mountWithHydration(html: string, setup: () => any) {
|
|
|
const container = document.createElement('div')
|
|
const container = document.createElement('div')
|
|
|
|
|
+ document.body.appendChild(container)
|
|
|
container.innerHTML = html
|
|
container.innerHTML = html
|
|
|
const app = createVaporSSRApp({
|
|
const app = createVaporSSRApp({
|
|
|
setup,
|
|
setup,
|
|
@@ -14,17 +24,19 @@ function mountWithHydration(html: string, setup: () => any) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// const triggerEvent = (type: string, el: Element) => {
|
|
|
|
|
-// const event = new Event(type)
|
|
|
|
|
-// el.dispatchEvent(event)
|
|
|
|
|
-// }
|
|
|
|
|
|
|
+const triggerEvent = (type: string, el: Element) => {
|
|
|
|
|
+ const event = new Event(type, { bubbles: true })
|
|
|
|
|
+ el.dispatchEvent(event)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
describe('SSR hydration', () => {
|
|
describe('SSR hydration', () => {
|
|
|
|
|
+ delegateEvents('click')
|
|
|
|
|
+
|
|
|
beforeEach(() => {
|
|
beforeEach(() => {
|
|
|
document.body.innerHTML = ''
|
|
document.body.innerHTML = ''
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- test('text', async () => {
|
|
|
|
|
|
|
+ test('root text', async () => {
|
|
|
const msg = ref('foo')
|
|
const msg = ref('foo')
|
|
|
const t = template(' ')
|
|
const t = template(' ')
|
|
|
const { container } = mountWithHydration('foo', () => {
|
|
const { container } = mountWithHydration('foo', () => {
|
|
@@ -38,128 +50,99 @@ describe('SSR hydration', () => {
|
|
|
expect(container.textContent).toBe('bar')
|
|
expect(container.textContent).toBe('bar')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- test('empty text', async () => {
|
|
|
|
|
- const t0 = template('<div></div>', true)
|
|
|
|
|
- const { container } = mountWithHydration('<div></div>', () => t0())
|
|
|
|
|
- expect(container.innerHTML).toBe('<div></div>')
|
|
|
|
|
- expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- test('comment', () => {
|
|
|
|
|
|
|
+ test('root comment', () => {
|
|
|
const t0 = template('<!---->')
|
|
const t0 = template('<!---->')
|
|
|
const { container } = mountWithHydration('<!---->', () => t0())
|
|
const { container } = mountWithHydration('<!---->', () => t0())
|
|
|
expect(container.innerHTML).toBe('<!---->')
|
|
expect(container.innerHTML).toBe('<!---->')
|
|
|
expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
|
|
expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- // test('static before text', () => {
|
|
|
|
|
- // const t0 = template(' A ')
|
|
|
|
|
- // const t1 = template('<span>foo bar</span>')
|
|
|
|
|
- // const t2 = template(' ')
|
|
|
|
|
- // const msg = ref('hello')
|
|
|
|
|
- // const { container } = mountWithHydration(
|
|
|
|
|
- // ' A <span>foo bar</span>hello',
|
|
|
|
|
- // () => {
|
|
|
|
|
- // const n0 = t0()
|
|
|
|
|
- // const n1 = t1()
|
|
|
|
|
- // const n2 = t2()
|
|
|
|
|
- // const n3 = createTextNode()
|
|
|
|
|
- // renderEffect(() => setText(n3, toDisplayString(msg.value)))
|
|
|
|
|
- // return [n0, n1, n2, n3]
|
|
|
|
|
- // },
|
|
|
|
|
- // )
|
|
|
|
|
- // })
|
|
|
|
|
-
|
|
|
|
|
- // test('static (multiple elements)', () => {
|
|
|
|
|
- // const staticContent = '<div></div><span>hello</span>'
|
|
|
|
|
- // const html = `<div><div>hi</div>` + staticContent + `<div>ho</div></div>`
|
|
|
|
|
-
|
|
|
|
|
- // const n1 = h('div', 'hi')
|
|
|
|
|
- // const s = createStaticVNode('', 2)
|
|
|
|
|
- // const n2 = h('div', 'ho')
|
|
|
|
|
-
|
|
|
|
|
- // const { container } = mountWithHydration(html, () => h('div', [n1, s, n2]))
|
|
|
|
|
-
|
|
|
|
|
- // const div = container.firstChild!
|
|
|
|
|
-
|
|
|
|
|
- // expect(n1.el).toBe(div.firstChild)
|
|
|
|
|
- // expect(n2.el).toBe(div.lastChild)
|
|
|
|
|
- // expect(s.el).toBe(div.childNodes[1])
|
|
|
|
|
- // expect(s.anchor).toBe(div.childNodes[2])
|
|
|
|
|
- // expect(s.children).toBe(staticContent)
|
|
|
|
|
- // })
|
|
|
|
|
-
|
|
|
|
|
- // // #6008
|
|
|
|
|
- // test('static (with text node as starting node)', () => {
|
|
|
|
|
- // const html = ` A <span>foo</span> B`
|
|
|
|
|
- // const { vnode, container } = mountWithHydration(html, () =>
|
|
|
|
|
- // createStaticVNode(` A <span>foo</span> B`, 3),
|
|
|
|
|
- // )
|
|
|
|
|
- // expect(vnode.el).toBe(container.firstChild)
|
|
|
|
|
- // expect(vnode.anchor).toBe(container.lastChild)
|
|
|
|
|
- // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
|
|
|
|
|
- // })
|
|
|
|
|
-
|
|
|
|
|
- // test('static with content adoption', () => {
|
|
|
|
|
- // const html = ` A <span>foo</span> B`
|
|
|
|
|
- // const { vnode, container } = mountWithHydration(html, () =>
|
|
|
|
|
- // createStaticVNode(``, 3),
|
|
|
|
|
- // )
|
|
|
|
|
- // expect(vnode.el).toBe(container.firstChild)
|
|
|
|
|
- // expect(vnode.anchor).toBe(container.lastChild)
|
|
|
|
|
- // expect(vnode.children).toBe(html)
|
|
|
|
|
- // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
|
|
|
|
|
- // })
|
|
|
|
|
-
|
|
|
|
|
- // test('element with text children', async () => {
|
|
|
|
|
- // const msg = ref('foo')
|
|
|
|
|
- // const { vnode, container } = mountWithHydration(
|
|
|
|
|
- // '<div class="foo">foo</div>',
|
|
|
|
|
- // () => h('div', { class: msg.value }, msg.value),
|
|
|
|
|
- // )
|
|
|
|
|
- // expect(vnode.el).toBe(container.firstChild)
|
|
|
|
|
- // expect(container.firstChild!.textContent).toBe('foo')
|
|
|
|
|
- // msg.value = 'bar'
|
|
|
|
|
- // await nextTick()
|
|
|
|
|
- // expect(container.innerHTML).toBe(`<div class="bar">bar</div>`)
|
|
|
|
|
- // })
|
|
|
|
|
|
|
+ test('root with mixed element and text', async () => {
|
|
|
|
|
+ const t0 = template(' A')
|
|
|
|
|
+ const t1 = template('<span>foo bar</span>')
|
|
|
|
|
+ const t2 = template(' ')
|
|
|
|
|
+ const msg = ref('hello')
|
|
|
|
|
+ const { container } = mountWithHydration(
|
|
|
|
|
+ ' A<span>foo bar</span>hello',
|
|
|
|
|
+ () => {
|
|
|
|
|
+ const n0 = t0()
|
|
|
|
|
+ const n1 = t1()
|
|
|
|
|
+ const n2 = t2()
|
|
|
|
|
+ renderEffect(() => setText(n2 as Text, toDisplayString(msg.value)))
|
|
|
|
|
+ return [n0, n1, n2]
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
+ expect(container.innerHTML).toBe(' A<span>foo bar</span>hello')
|
|
|
|
|
+ msg.value = 'bar'
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ expect(container.innerHTML).toBe(' A<span>foo bar</span>bar')
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
- // // #7285
|
|
|
|
|
- // test('element with multiple continuous text vnodes', async () => {
|
|
|
|
|
- // // should no mismatch warning
|
|
|
|
|
- // const { container } = mountWithHydration('<div>foo0o</div>', () =>
|
|
|
|
|
- // h('div', ['fo', createTextVNode('o'), 0, 'o']),
|
|
|
|
|
- // )
|
|
|
|
|
- // expect(container.textContent).toBe('foo0o')
|
|
|
|
|
- // })
|
|
|
|
|
|
|
+ test('empty element', async () => {
|
|
|
|
|
+ const t0 = template('<div></div>', true)
|
|
|
|
|
+ const { container } = mountWithHydration('<div></div>', () => t0())
|
|
|
|
|
+ expect(container.innerHTML).toBe('<div></div>')
|
|
|
|
|
+ expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
- // test('element with elements children', async () => {
|
|
|
|
|
- // const msg = ref('foo')
|
|
|
|
|
- // const fn = vi.fn()
|
|
|
|
|
- // const { vnode, container } = mountWithHydration(
|
|
|
|
|
- // '<div><span>foo</span><span class="foo"></span></div>',
|
|
|
|
|
- // () =>
|
|
|
|
|
- // h('div', [
|
|
|
|
|
- // h('span', msg.value),
|
|
|
|
|
- // h('span', { class: msg.value, onClick: fn }),
|
|
|
|
|
- // ]),
|
|
|
|
|
- // )
|
|
|
|
|
- // expect(vnode.el).toBe(container.firstChild)
|
|
|
|
|
- // expect((vnode.children as VNode[])[0].el).toBe(
|
|
|
|
|
- // container.firstChild!.childNodes[0],
|
|
|
|
|
- // )
|
|
|
|
|
- // expect((vnode.children as VNode[])[1].el).toBe(
|
|
|
|
|
- // container.firstChild!.childNodes[1],
|
|
|
|
|
- // )
|
|
|
|
|
|
|
+ test('element with text children', async () => {
|
|
|
|
|
+ const t0 = template('<div> </div>', true)
|
|
|
|
|
+ const msg = ref('foo')
|
|
|
|
|
+ const { container } = mountWithHydration(
|
|
|
|
|
+ '<div class="foo">foo</div>',
|
|
|
|
|
+ () => {
|
|
|
|
|
+ const n0 = t0() as Element
|
|
|
|
|
+ const x0 = child(n0) as Text
|
|
|
|
|
+ renderEffect(() => {
|
|
|
|
|
+ const _msg = msg.value
|
|
|
|
|
+
|
|
|
|
|
+ setText(x0, toDisplayString(_msg))
|
|
|
|
|
+ setClass(n0, _msg)
|
|
|
|
|
+ })
|
|
|
|
|
+ return n0
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
+ expect(container.innerHTML).toBe(`<div class="foo">foo</div>`)
|
|
|
|
|
+ msg.value = 'bar'
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ expect(container.innerHTML).toBe(`<div class="bar">bar</div>`)
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
- // // event handler
|
|
|
|
|
- // triggerEvent('click', vnode.el.querySelector('.foo')!)
|
|
|
|
|
- // expect(fn).toHaveBeenCalled()
|
|
|
|
|
|
|
+ test('element with elements children', async () => {
|
|
|
|
|
+ const t0 = template('<div><span> </span><span></span></div>', true)
|
|
|
|
|
+ const msg = ref('foo')
|
|
|
|
|
+ const fn = vi.fn()
|
|
|
|
|
+ const { container } = mountWithHydration(
|
|
|
|
|
+ '<div><span>foo</span><span class="foo"></span></div>',
|
|
|
|
|
+ () => {
|
|
|
|
|
+ const n2 = t0() as Element
|
|
|
|
|
+ const n0 = child(n2) as Element
|
|
|
|
|
+ const n1 = next(n0) as Element
|
|
|
|
|
+ const x0 = child(n0) as Text
|
|
|
|
|
+ ;(n1 as any).$evtclick = fn
|
|
|
|
|
+ renderEffect(() => {
|
|
|
|
|
+ const _msg = msg.value
|
|
|
|
|
+
|
|
|
|
|
+ setText(x0, toDisplayString(_msg))
|
|
|
|
|
+ setClass(n1, _msg)
|
|
|
|
|
+ })
|
|
|
|
|
+ return n2
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
+ expect(container.innerHTML).toBe(
|
|
|
|
|
+ `<div><span>foo</span><span class="foo"></span></div>`,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // event handler
|
|
|
|
|
+ triggerEvent('click', container.querySelector('.foo')!)
|
|
|
|
|
+ expect(fn).toHaveBeenCalled()
|
|
|
|
|
|
|
|
- // msg.value = 'bar'
|
|
|
|
|
- // await nextTick()
|
|
|
|
|
- // expect(vnode.el.innerHTML).toBe(`<span>bar</span><span class="bar"></span>`)
|
|
|
|
|
- // })
|
|
|
|
|
|
|
+ msg.value = 'bar'
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ expect(container.innerHTML).toBe(
|
|
|
|
|
+ `<div><span>bar</span><span class="bar"></span></div>`,
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
// test('element with ref', () => {
|
|
// test('element with ref', () => {
|
|
|
// const el = ref()
|
|
// const el = ref()
|