import { HMRRuntime } from '../src/hmr'
import '../src/hmr'
import { ComponentOptions, InternalRenderFunction } 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 InternalRenderFunction
render._rc = true // isRuntimeCompiled
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(``)
}
createRecord(childId, Child)
const Parent: ComponentOptions = {
__hmrId: parentId,
data() {
return { count: 0 }
},
components: { Child },
render: compileToFunction(
`
{{ count }}{{ count }}
`
)
}
createRecord(parentId, Parent)
render(h(Parent), root)
expect(serializeInner(root)).toBe(`00
`)
// 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(`11
`)
// // Update text while preserving state
// rerender(
// parentId,
// compileToFunction(
// `{{ count }}!{{ count }}
`
// )
// )
// expect(serializeInner(root)).toBe(`1!1
`)
// Should force child update on slot content change
rerender(
parentId,
compileToFunction(
`{{ count }}!{{ count }}!
`
)
)
expect(serializeInner(root)).toBe(`1!1!
`)
// Should force update element children despite block optimization
rerender(
parentId,
compileToFunction(
`{{ count }}{{ count }}
{{ count }}!
`
)
)
expect(serializeInner(root)).toBe(`111!
`)
// Should force update child slot elements
rerender(
parentId,
compileToFunction(
`
{{ count }}
`
)
)
expect(serializeInner(root)).toBe(`1
`)
})
test('reload', async () => {
const root = nodeOps.createElement('div')
const childId = 'test3-child'
const unmountSpy = jest.fn()
const mountSpy = jest.fn()
const Child: ComponentOptions = {
__hmrId: childId,
data() {
return { count: 0 }
},
unmounted: unmountSpy,
render: compileToFunction(`{{ count }}
`)
}
createRecord(childId, Child)
const Parent: ComponentOptions = {
render: () => h(Child)
}
render(h(Parent), root)
expect(serializeInner(root)).toBe(`0
`)
reload(childId, {
__hmrId: childId,
data() {
return { count: 1 }
},
mounted: mountSpy,
render: compileToFunction(`{{ count }}
`)
})
await nextTick()
expect(serializeInner(root)).toBe(`1
`)
expect(unmountSpy).toHaveBeenCalledTimes(1)
expect(mountSpy).toHaveBeenCalledTimes(1)
})
})