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) }) })