Skip to content

Commit a2415de

Browse files
committed
wip(vapor): text hydration tests
1 parent 97c40a6 commit a2415de

File tree

3 files changed

+104
-120
lines changed

3 files changed

+104
-120
lines changed

packages/compiler-vapor/src/transforms/transformChildren.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ function processDynamicChildren(context: TransformContext<ElementNode>) {
6969

7070
prevDynamics[0].flags -= DynamicFlag.NON_TEMPLATE
7171
const anchor = (prevDynamics[0].anchor = context.increaseId())
72-
7372
context.registerOperation({
7473
type: IRNodeTypes.INSERT_NODE,
7574
elements: prevDynamics.map(child => child.id!),

packages/runtime-vapor/__tests__/hydration.spec.ts

Lines changed: 102 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
// import { type SSRContext, renderToString } from '@vue/server-renderer'
2-
import { createVaporSSRApp, renderEffect, setText, template } from '../src'
3-
import { nextTick, ref } from '@vue/runtime-dom'
2+
import {
3+
child,
4+
createVaporSSRApp,
5+
delegateEvents,
6+
next,
7+
renderEffect,
8+
setClass,
9+
setText,
10+
template,
11+
} from '../src'
12+
import { nextTick, ref, toDisplayString } from '@vue/runtime-dom'
413

514
function mountWithHydration(html: string, setup: () => any) {
615
const container = document.createElement('div')
16+
document.body.appendChild(container)
717
container.innerHTML = html
818
const app = createVaporSSRApp({
919
setup,
@@ -14,17 +24,19 @@ function mountWithHydration(html: string, setup: () => any) {
1424
}
1525
}
1626

17-
// const triggerEvent = (type: string, el: Element) => {
18-
// const event = new Event(type)
19-
// el.dispatchEvent(event)
20-
// }
27+
const triggerEvent = (type: string, el: Element) => {
28+
const event = new Event(type, { bubbles: true })
29+
el.dispatchEvent(event)
30+
}
2131

2232
describe('SSR hydration', () => {
33+
delegateEvents('click')
34+
2335
beforeEach(() => {
2436
document.body.innerHTML = ''
2537
})
2638

27-
test('text', async () => {
39+
test('root text', async () => {
2840
const msg = ref('foo')
2941
const t = template(' ')
3042
const { container } = mountWithHydration('foo', () => {
@@ -38,128 +50,99 @@ describe('SSR hydration', () => {
3850
expect(container.textContent).toBe('bar')
3951
})
4052

41-
test('empty text', async () => {
42-
const t0 = template('<div></div>', true)
43-
const { container } = mountWithHydration('<div></div>', () => t0())
44-
expect(container.innerHTML).toBe('<div></div>')
45-
expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
46-
})
47-
48-
test('comment', () => {
53+
test('root comment', () => {
4954
const t0 = template('<!---->')
5055
const { container } = mountWithHydration('<!---->', () => t0())
5156
expect(container.innerHTML).toBe('<!---->')
5257
expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
5358
})
5459

55-
// test('static before text', () => {
56-
// const t0 = template(' A ')
57-
// const t1 = template('<span>foo bar</span>')
58-
// const t2 = template(' ')
59-
// const msg = ref('hello')
60-
// const { container } = mountWithHydration(
61-
// ' A <span>foo bar</span>hello',
62-
// () => {
63-
// const n0 = t0()
64-
// const n1 = t1()
65-
// const n2 = t2()
66-
// const n3 = createTextNode()
67-
// renderEffect(() => setText(n3, toDisplayString(msg.value)))
68-
// return [n0, n1, n2, n3]
69-
// },
70-
// )
71-
// })
72-
73-
// test('static (multiple elements)', () => {
74-
// const staticContent = '<div></div><span>hello</span>'
75-
// const html = `<div><div>hi</div>` + staticContent + `<div>ho</div></div>`
76-
77-
// const n1 = h('div', 'hi')
78-
// const s = createStaticVNode('', 2)
79-
// const n2 = h('div', 'ho')
80-
81-
// const { container } = mountWithHydration(html, () => h('div', [n1, s, n2]))
82-
83-
// const div = container.firstChild!
84-
85-
// expect(n1.el).toBe(div.firstChild)
86-
// expect(n2.el).toBe(div.lastChild)
87-
// expect(s.el).toBe(div.childNodes[1])
88-
// expect(s.anchor).toBe(div.childNodes[2])
89-
// expect(s.children).toBe(staticContent)
90-
// })
91-
92-
// // #6008
93-
// test('static (with text node as starting node)', () => {
94-
// const html = ` A <span>foo</span> B`
95-
// const { vnode, container } = mountWithHydration(html, () =>
96-
// createStaticVNode(` A <span>foo</span> B`, 3),
97-
// )
98-
// expect(vnode.el).toBe(container.firstChild)
99-
// expect(vnode.anchor).toBe(container.lastChild)
100-
// expect(`Hydration node mismatch`).not.toHaveBeenWarned()
101-
// })
102-
103-
// test('static with content adoption', () => {
104-
// const html = ` A <span>foo</span> B`
105-
// const { vnode, container } = mountWithHydration(html, () =>
106-
// createStaticVNode(``, 3),
107-
// )
108-
// expect(vnode.el).toBe(container.firstChild)
109-
// expect(vnode.anchor).toBe(container.lastChild)
110-
// expect(vnode.children).toBe(html)
111-
// expect(`Hydration node mismatch`).not.toHaveBeenWarned()
112-
// })
113-
114-
// test('element with text children', async () => {
115-
// const msg = ref('foo')
116-
// const { vnode, container } = mountWithHydration(
117-
// '<div class="foo">foo</div>',
118-
// () => h('div', { class: msg.value }, msg.value),
119-
// )
120-
// expect(vnode.el).toBe(container.firstChild)
121-
// expect(container.firstChild!.textContent).toBe('foo')
122-
// msg.value = 'bar'
123-
// await nextTick()
124-
// expect(container.innerHTML).toBe(`<div class="bar">bar</div>`)
125-
// })
60+
test('root with mixed element and text', async () => {
61+
const t0 = template(' A')
62+
const t1 = template('<span>foo bar</span>')
63+
const t2 = template(' ')
64+
const msg = ref('hello')
65+
const { container } = mountWithHydration(
66+
' A<span>foo bar</span>hello',
67+
() => {
68+
const n0 = t0()
69+
const n1 = t1()
70+
const n2 = t2()
71+
renderEffect(() => setText(n2 as Text, toDisplayString(msg.value)))
72+
return [n0, n1, n2]
73+
},
74+
)
75+
expect(container.innerHTML).toBe(' A<span>foo bar</span>hello')
76+
msg.value = 'bar'
77+
await nextTick()
78+
expect(container.innerHTML).toBe(' A<span>foo bar</span>bar')
79+
})
12680

127-
// // #7285
128-
// test('element with multiple continuous text vnodes', async () => {
129-
// // should no mismatch warning
130-
// const { container } = mountWithHydration('<div>foo0o</div>', () =>
131-
// h('div', ['fo', createTextVNode('o'), 0, 'o']),
132-
// )
133-
// expect(container.textContent).toBe('foo0o')
134-
// })
81+
test('empty element', async () => {
82+
const t0 = template('<div></div>', true)
83+
const { container } = mountWithHydration('<div></div>', () => t0())
84+
expect(container.innerHTML).toBe('<div></div>')
85+
expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
86+
})
13587

136-
// test('element with elements children', async () => {
137-
// const msg = ref('foo')
138-
// const fn = vi.fn()
139-
// const { vnode, container } = mountWithHydration(
140-
// '<div><span>foo</span><span class="foo"></span></div>',
141-
// () =>
142-
// h('div', [
143-
// h('span', msg.value),
144-
// h('span', { class: msg.value, onClick: fn }),
145-
// ]),
146-
// )
147-
// expect(vnode.el).toBe(container.firstChild)
148-
// expect((vnode.children as VNode[])[0].el).toBe(
149-
// container.firstChild!.childNodes[0],
150-
// )
151-
// expect((vnode.children as VNode[])[1].el).toBe(
152-
// container.firstChild!.childNodes[1],
153-
// )
88+
test('element with text children', async () => {
89+
const t0 = template('<div> </div>', true)
90+
const msg = ref('foo')
91+
const { container } = mountWithHydration(
92+
'<div class="foo">foo</div>',
93+
() => {
94+
const n0 = t0() as Element
95+
const x0 = child(n0) as Text
96+
renderEffect(() => {
97+
const _msg = msg.value
98+
99+
setText(x0, toDisplayString(_msg))
100+
setClass(n0, _msg)
101+
})
102+
return n0
103+
},
104+
)
105+
expect(container.innerHTML).toBe(`<div class="foo">foo</div>`)
106+
msg.value = 'bar'
107+
await nextTick()
108+
expect(container.innerHTML).toBe(`<div class="bar">bar</div>`)
109+
})
154110

155-
// // event handler
156-
// triggerEvent('click', vnode.el.querySelector('.foo')!)
157-
// expect(fn).toHaveBeenCalled()
111+
test('element with elements children', async () => {
112+
const t0 = template('<div><span> </span><span></span></div>', true)
113+
const msg = ref('foo')
114+
const fn = vi.fn()
115+
const { container } = mountWithHydration(
116+
'<div><span>foo</span><span class="foo"></span></div>',
117+
() => {
118+
const n2 = t0() as Element
119+
const n0 = child(n2) as Element
120+
const n1 = next(n0) as Element
121+
const x0 = child(n0) as Text
122+
;(n1 as any).$evtclick = fn
123+
renderEffect(() => {
124+
const _msg = msg.value
125+
126+
setText(x0, toDisplayString(_msg))
127+
setClass(n1, _msg)
128+
})
129+
return n2
130+
},
131+
)
132+
expect(container.innerHTML).toBe(
133+
`<div><span>foo</span><span class="foo"></span></div>`,
134+
)
135+
136+
// event handler
137+
triggerEvent('click', container.querySelector('.foo')!)
138+
expect(fn).toHaveBeenCalled()
158139

159-
// msg.value = 'bar'
160-
// await nextTick()
161-
// expect(vnode.el.innerHTML).toBe(`<span>bar</span><span class="bar"></span>`)
162-
// })
140+
msg.value = 'bar'
141+
await nextTick()
142+
expect(container.innerHTML).toBe(
143+
`<div><span>bar</span><span class="bar"></span></div>`,
144+
)
145+
})
163146

164147
// test('element with ref', () => {
165148
// const el = ref()

packages/runtime-vapor/src/dom/hydration.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ function adoptHydrationNodeImpl(
116116
!template.startsWith((adopted as Text).data))
117117
) {
118118
// TODO recover and provide more info
119+
console.error(`adopted: `, adopted)
120+
console.error(`template: ${template}`)
119121
throw new Error('hydration mismatch!')
120122
}
121123
}

0 commit comments

Comments
 (0)