Skip to content

Commit 36c19b5

Browse files
committed
test(hmr): port hmr tests
1 parent 584f25f commit 36c19b5

File tree

1 file changed

+206
-2
lines changed

1 file changed

+206
-2
lines changed

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

Lines changed: 206 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,225 @@
11
// TODO: port tests from packages/runtime-core/__tests__/hmr.spec.ts
22

3-
import { type HMRRuntime, ref } from '@vue/runtime-dom'
3+
import {
4+
type HMRRuntime,
5+
nextTick,
6+
ref,
7+
toDisplayString,
8+
} from '@vue/runtime-dom'
49
import { makeRender } from './_utils'
510
import {
611
child,
712
createComponent,
13+
createComponentWithFallback,
14+
createInvoker,
15+
createSlot,
16+
defineVaporComponent,
17+
delegateEvents,
18+
next,
819
renderEffect,
20+
setInsertionState,
921
setText,
1022
template,
23+
txt,
24+
withVaporCtx,
1125
} from '@vue/runtime-vapor'
1226

1327
declare var __VUE_HMR_RUNTIME__: HMRRuntime
14-
const { createRecord, reload } = __VUE_HMR_RUNTIME__
28+
const { createRecord, rerender, reload } = __VUE_HMR_RUNTIME__
1529

1630
const define = makeRender()
1731

32+
const triggerEvent = (type: string, el: Element) => {
33+
const event = new Event(type, { bubbles: true })
34+
el.dispatchEvent(event)
35+
}
36+
delegateEvents('click')
37+
38+
beforeEach(() => {
39+
document.body.innerHTML = ''
40+
})
41+
1842
describe('hot module replacement', () => {
43+
test('inject global runtime', () => {
44+
expect(createRecord).toBeDefined()
45+
expect(rerender).toBeDefined()
46+
expect(reload).toBeDefined()
47+
})
48+
49+
test('createRecord', () => {
50+
expect(createRecord('test1', {})).toBe(true)
51+
// if id has already been created, should return false
52+
expect(createRecord('test1', {})).toBe(false)
53+
})
54+
55+
test('rerender', async () => {
56+
const root = document.createElement('div')
57+
const parentId = 'test2-parent'
58+
const childId = 'test2-child'
59+
document.body.appendChild(root)
60+
61+
const Child = defineVaporComponent({
62+
__hmrId: childId,
63+
render() {
64+
const n1 = template('<div></div>', true)() as any
65+
setInsertionState(n1, null, true)
66+
createSlot('default', null)
67+
return n1
68+
},
69+
})
70+
createRecord(childId, Child as any)
71+
72+
const Parent = defineVaporComponent({
73+
__hmrId: parentId,
74+
setup() {
75+
const count = ref(0)
76+
return { count }
77+
},
78+
render(ctx) {
79+
const n3 = template('<div> </div>', true)() as any
80+
const n0 = child(n3) as any
81+
setInsertionState(n3, 1, true)
82+
createComponent(Child, null, {
83+
default: withVaporCtx(() => {
84+
const n1 = template(' ')() as any
85+
renderEffect(() => setText(n1, toDisplayString(ctx.count)))
86+
return n1
87+
}),
88+
})
89+
n3.$evtclick = createInvoker(() => ctx.count++)
90+
renderEffect(() => setText(n0, toDisplayString(ctx.count)))
91+
return n3
92+
},
93+
})
94+
createRecord(parentId, Parent as any)
95+
96+
// render(h(Parent), root)
97+
const { mount } = define(Parent).create()
98+
mount(root)
99+
expect(root.innerHTML).toBe(`<div>0<div>0<!--slot--></div></div>`)
100+
101+
// Perform some state change. This change should be preserved after the
102+
// re-render!
103+
// triggerEvent(root.children[0] as TestElement, 'click')
104+
triggerEvent('click', root.children[0])
105+
await nextTick()
106+
expect(root.innerHTML).toBe(`<div>1<div>1<!--slot--></div></div>`)
107+
108+
// Update text while preserving state
109+
rerender(parentId, (ctx: any) => {
110+
const n3 = template('<div> </div>', true)() as any
111+
const n0 = child(n3) as any
112+
setInsertionState(n3, 1, true)
113+
createComponent(Child, null, {
114+
default: withVaporCtx(() => {
115+
const n1 = template(' ')() as any
116+
renderEffect(() => setText(n1, toDisplayString(ctx.count)))
117+
return n1
118+
}),
119+
})
120+
n3.$evtclick = createInvoker(() => ctx.count++)
121+
renderEffect(() => setText(n0, toDisplayString(ctx.count) + '!'))
122+
return n3
123+
})
124+
expect(root.innerHTML).toBe(`<div>1!<div>1<!--slot--></div></div>`)
125+
126+
// Should force child update on slot content change
127+
rerender(parentId, (ctx: any) => {
128+
const n3 = template('<div> </div>', true)() as any
129+
const n0 = child(n3) as any
130+
setInsertionState(n3, 1, true)
131+
createComponent(Child, null, {
132+
default: withVaporCtx(() => {
133+
const n1 = template(' ')() as any
134+
renderEffect(() => setText(n1, toDisplayString(ctx.count) + '!'))
135+
return n1
136+
}),
137+
})
138+
n3.$evtclick = createInvoker(() => ctx.count++)
139+
renderEffect(() => setText(n0, toDisplayString(ctx.count) + '!'))
140+
return n3
141+
})
142+
expect(root.innerHTML).toBe(`<div>1!<div>1!<!--slot--></div></div>`)
143+
144+
// Should force update element children despite block optimization
145+
rerender(parentId, (ctx: any) => {
146+
const n5 = template('<div> <span> </span></div>', true)() as any
147+
const n0 = child(n5) as any
148+
const n1 = next(n0) as any
149+
setInsertionState(n5, 2, true)
150+
createComponentWithFallback(Child, null, {
151+
default: withVaporCtx(() => {
152+
const n2 = template(' ')() as any
153+
renderEffect(() => setText(n2, toDisplayString(ctx.count) + '!'))
154+
return n2
155+
}),
156+
})
157+
const x1 = txt(n1) as any
158+
n5.$evtclick = createInvoker(() => ctx.count++)
159+
renderEffect(() => {
160+
const count = ctx.count
161+
setText(n0, toDisplayString(count))
162+
setText(x1, toDisplayString(count))
163+
})
164+
return n5
165+
})
166+
expect(root.innerHTML).toBe(
167+
`<div>1<span>1</span><div>1!<!--slot--></div></div>`,
168+
)
169+
170+
// Should force update child slot elements
171+
rerender(parentId, (ctx: any) => {
172+
const n2 = template('<div></div>', true)() as any
173+
setInsertionState(n2, null, true)
174+
createComponentWithFallback(Child, null, {
175+
default: withVaporCtx(() => {
176+
const n0 = template('<span> </span>')() as any
177+
const x0 = txt(n0) as any
178+
renderEffect(() => setText(x0, toDisplayString(ctx.count)))
179+
return n0
180+
}),
181+
})
182+
n2.$evtclick = createInvoker(() => ctx.count++)
183+
return n2
184+
})
185+
expect(root.innerHTML).toBe(
186+
`<div><div><span>1</span><!--slot--></div></div>`,
187+
)
188+
})
189+
190+
test('reload', async () => {
191+
// const root = nodeOps.createElement('div')
192+
// const childId = 'test3-child'
193+
// const unmountSpy = vi.fn()
194+
// const mountSpy = vi.fn()
195+
// const Child: ComponentOptions = {
196+
// __hmrId: childId,
197+
// data() {
198+
// return { count: 0 }
199+
// },
200+
// unmounted: unmountSpy,
201+
// render: compileToFunction(`<div @click="count++">{{ count }}</div>`),
202+
// }
203+
// createRecord(childId, Child)
204+
// const Parent: ComponentOptions = {
205+
// render: () => h(Child),
206+
// }
207+
// render(h(Parent), root)
208+
// expect(serializeInner(root)).toBe(`<div>0</div>`)
209+
// reload(childId, {
210+
// __hmrId: childId,
211+
// data() {
212+
// return { count: 1 }
213+
// },
214+
// mounted: mountSpy,
215+
// render: compileToFunction(`<div @click="count++">{{ count }}</div>`),
216+
// })
217+
// await nextTick()
218+
// expect(serializeInner(root)).toBe(`<div>1</div>`)
219+
// expect(unmountSpy).toHaveBeenCalledTimes(1)
220+
// expect(mountSpy).toHaveBeenCalledTimes(1)
221+
})
222+
19223
test('child reload + parent reload', async () => {
20224
const root = document.createElement('div')
21225
const childId = 'test1-child-reload'

0 commit comments

Comments
 (0)