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(`
0
0
`) // 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(`
1
1
`) // // 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(`
11
1!
`) // 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) }) // #1156 - static nodes should retain DOM element reference across updates // when HMR is active test('static el reference', async () => { const root = nodeOps.createElement('div') const id = 'test-static-el' const template = `
{{ count }}
` const Comp: ComponentOptions = { __hmrId: id, data() { return { count: 0 } }, render: compileToFunction(template) } createRecord(id, Comp) render(h(Comp), root) expect(serializeInner(root)).toBe( `
0
` ) // 1. click to trigger update triggerEvent((root as any).children[0].children[1], 'click') await nextTick() expect(serializeInner(root)).toBe( `
1
` ) // 2. trigger HMR rerender( id, compileToFunction(template.replace(`
1
` ) }) // #1157 - component should force full props update when HMR is active test('force update child component w/ static props', () => { const root = nodeOps.createElement('div') const parentId = 'test-force-props-parent' const childId = 'test-force-props-child' const Child: ComponentOptions = { __hmrId: childId, props: { msg: String }, render: compileToFunction(`
{{ msg }}
`) } createRecord(childId, Child) const Parent: ComponentOptions = { __hmrId: parentId, components: { Child }, render: compileToFunction(``) } createRecord(parentId, Parent) render(h(Parent), root) expect(serializeInner(root)).toBe(`
foo
`) rerender(parentId, compileToFunction(``)) expect(serializeInner(root)).toBe(`
bar
`) }) })