test: test hot module replacement
This commit is contained in:
150
packages/runtime-core/__tests__/hmr.spec.ts
Normal file
150
packages/runtime-core/__tests__/hmr.spec.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { HMRRuntime } from '../src/hmr'
|
||||
import '../src/hmr'
|
||||
import { ComponentOptions, RenderFunction } from '../src/component'
|
||||
import {
|
||||
render,
|
||||
nodeOps,
|
||||
h,
|
||||
serializeInner,
|
||||
triggerEvent,
|
||||
TestElement,
|
||||
nextTick
|
||||
} from '@vue/runtime-test'
|
||||
import * as runtimeTest from '@vue/runtime-test'
|
||||
import { baseCompile } from '@vue/compiler-core'
|
||||
|
||||
declare var __VUE_HMR_RUNTIME__: HMRRuntime
|
||||
const { createRecord, rerender, reload } = __VUE_HMR_RUNTIME__
|
||||
|
||||
function compileToFunction(template: string) {
|
||||
const { code } = baseCompile(template)
|
||||
const render = new Function('Vue', code)(runtimeTest) as RenderFunction
|
||||
render.isRuntimeCompiled = true
|
||||
return render
|
||||
}
|
||||
|
||||
describe('hot module replacement', () => {
|
||||
test('inject global runtime', () => {
|
||||
expect(createRecord).toBeDefined()
|
||||
expect(rerender).toBeDefined()
|
||||
expect(reload).toBeDefined()
|
||||
})
|
||||
|
||||
test('createRecord', () => {
|
||||
expect(createRecord('test1', {})).toBe(true)
|
||||
// if id has already been created, should return false
|
||||
expect(createRecord('test1', {})).toBe(false)
|
||||
})
|
||||
|
||||
test('rerender', async () => {
|
||||
const root = nodeOps.createElement('div')
|
||||
const parentId = 'test2-parent'
|
||||
const childId = 'test2-child'
|
||||
|
||||
const Child: ComponentOptions = {
|
||||
__hmrId: childId,
|
||||
render: compileToFunction(`<slot/>`)
|
||||
}
|
||||
createRecord(childId, Child)
|
||||
|
||||
const Parent: ComponentOptions = {
|
||||
__hmrId: parentId,
|
||||
data() {
|
||||
return { count: 0 }
|
||||
},
|
||||
components: { Child },
|
||||
render: compileToFunction(
|
||||
`<div @click="count++">{{ count }}<Child>{{ count }}</Child></div>`
|
||||
)
|
||||
}
|
||||
createRecord(parentId, Parent)
|
||||
|
||||
render(h(Parent), root)
|
||||
expect(serializeInner(root)).toBe(`<div>0<!---->0<!----></div>`)
|
||||
|
||||
// Perform some state change. This change should be preserved after the
|
||||
// re-render!
|
||||
triggerEvent(root.children[0] as TestElement, 'click')
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`<div>1<!---->1<!----></div>`)
|
||||
|
||||
// Update text while preserving state
|
||||
rerender(
|
||||
parentId,
|
||||
compileToFunction(
|
||||
`<div @click="count++">{{ count }}!<Child>{{ count }}</Child></div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>1!<!---->1<!----></div>`)
|
||||
|
||||
// Should force child update on slot content change
|
||||
rerender(
|
||||
parentId,
|
||||
compileToFunction(
|
||||
`<div @click="count++">{{ count }}!<Child>{{ count }}!</Child></div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>1!<!---->1!<!----></div>`)
|
||||
|
||||
// Should force update element children despite block optimization
|
||||
rerender(
|
||||
parentId,
|
||||
compileToFunction(
|
||||
`<div @click="count++">{{ count }}<span>{{ count }}</span>
|
||||
<Child>{{ count }}!</Child>
|
||||
</div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<div>1<span>1</span><!---->1!<!----></div>`
|
||||
)
|
||||
|
||||
// Should force update child slot elements
|
||||
rerender(
|
||||
parentId,
|
||||
compileToFunction(
|
||||
`<div @click="count++">
|
||||
<Child><span>{{ count }}</span></Child>
|
||||
</div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div><!----><span>1</span><!----></div>`)
|
||||
})
|
||||
|
||||
test('reload', async () => {
|
||||
const root = nodeOps.createElement('div')
|
||||
const childId = 'test3-child'
|
||||
const unmoutSpy = jest.fn()
|
||||
const mountSpy = jest.fn()
|
||||
|
||||
const Child: ComponentOptions = {
|
||||
__hmrId: childId,
|
||||
data() {
|
||||
return { count: 0 }
|
||||
},
|
||||
unmounted: unmoutSpy,
|
||||
render: compileToFunction(`<div @click="count++">{{ count }}</div>`)
|
||||
}
|
||||
createRecord(childId, Child)
|
||||
|
||||
const Parent: ComponentOptions = {
|
||||
render: () => h(Child)
|
||||
}
|
||||
|
||||
render(h(Parent), root)
|
||||
expect(serializeInner(root)).toBe(`<div>0</div>`)
|
||||
|
||||
reload(childId, {
|
||||
__hmrId: childId,
|
||||
data() {
|
||||
return { count: 1 }
|
||||
},
|
||||
mounted: mountSpy,
|
||||
render: compileToFunction(`<div @click="count++">{{ count }}</div>`)
|
||||
})
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`<div>1</div>`)
|
||||
expect(unmoutSpy).toHaveBeenCalledTimes(1)
|
||||
expect(mountSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user