test(runtime-core): tests for vnode hooks

This commit is contained in:
Evan You 2020-03-18 16:35:04 -04:00
parent ebc1ca8eff
commit 811f28a7d1
2 changed files with 124 additions and 21 deletions

View File

@ -0,0 +1,98 @@
import {
h,
render,
nodeOps,
VNodeProps,
TestElement,
NodeTypes,
VNode
} from '@vue/runtime-test'
describe('renderer: vnode hooks', () => {
function assertHooks(hooks: VNodeProps, vnode1: VNode, vnode2: VNode) {
const root = nodeOps.createElement('div')
render(vnode1, root)
expect(hooks.onVnodeBeforeMount).toHaveBeenCalledWith(vnode1, null)
expect(hooks.onVnodeMounted).toHaveBeenCalledWith(vnode1, null)
expect(hooks.onVnodeBeforeUpdate).not.toHaveBeenCalled()
expect(hooks.onVnodeUpdated).not.toHaveBeenCalled()
expect(hooks.onVnodeBeforeUnmount).not.toHaveBeenCalled()
expect(hooks.onVnodeUnmounted).not.toHaveBeenCalled()
// update
render(vnode2, root)
expect(hooks.onVnodeBeforeMount).toHaveBeenCalledTimes(1)
expect(hooks.onVnodeMounted).toHaveBeenCalledTimes(1)
expect(hooks.onVnodeBeforeUpdate).toHaveBeenCalledWith(vnode2, vnode1)
expect(hooks.onVnodeUpdated).toHaveBeenCalledWith(vnode2, vnode1)
expect(hooks.onVnodeBeforeUnmount).not.toHaveBeenCalled()
expect(hooks.onVnodeUnmounted).not.toHaveBeenCalled()
// unmount
render(null, root)
expect(hooks.onVnodeBeforeMount).toHaveBeenCalledTimes(1)
expect(hooks.onVnodeMounted).toHaveBeenCalledTimes(1)
expect(hooks.onVnodeBeforeUpdate).toHaveBeenCalledTimes(1)
expect(hooks.onVnodeUpdated).toHaveBeenCalledTimes(1)
expect(hooks.onVnodeBeforeUnmount).toHaveBeenCalledWith(vnode2, null)
expect(hooks.onVnodeUnmounted).toHaveBeenCalledWith(vnode2, null)
}
test('should work on element', () => {
const hooks: VNodeProps = {
onVnodeBeforeMount: jest.fn(),
onVnodeMounted: jest.fn(),
onVnodeBeforeUpdate: jest.fn(vnode => {
expect((vnode.el as TestElement).children[0]).toMatchObject({
type: NodeTypes.TEXT,
text: 'foo'
})
}),
onVnodeUpdated: jest.fn(vnode => {
expect((vnode.el as TestElement).children[0]).toMatchObject({
type: NodeTypes.TEXT,
text: 'bar'
})
}),
onVnodeBeforeUnmount: jest.fn(),
onVnodeUnmounted: jest.fn()
}
assertHooks(hooks, h('div', hooks, 'foo'), h('div', hooks, 'bar'))
})
test('should work on component', () => {
const Comp = (props: { msg: string }) => props.msg
const hooks: VNodeProps = {
onVnodeBeforeMount: jest.fn(),
onVnodeMounted: jest.fn(),
onVnodeBeforeUpdate: jest.fn(vnode => {
expect(vnode.el as TestElement).toMatchObject({
type: NodeTypes.TEXT,
text: 'foo'
})
}),
onVnodeUpdated: jest.fn(vnode => {
expect(vnode.el as TestElement).toMatchObject({
type: NodeTypes.TEXT,
text: 'bar'
})
}),
onVnodeBeforeUnmount: jest.fn(),
onVnodeUnmounted: jest.fn()
}
assertHooks(
hooks,
h(Comp, {
...hooks,
msg: 'foo'
}),
h(Comp, {
...hooks,
msg: 'bar'
})
)
})
})

View File

@ -1152,6 +1152,7 @@ function baseCreateRenderer<
const nextTree = renderComponentRoot(instance) const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree const prevTree = instance.subTree
instance.subTree = nextTree instance.subTree = nextTree
next.el = vnode.el
// beforeUpdate hook // beforeUpdate hook
if (bu !== null) { if (bu !== null) {
invokeHooks(bu) invokeHooks(bu)
@ -1673,23 +1674,22 @@ function baseCreateRenderer<
setRef(ref, null, parentComponent, null) setRef(ref, null, parentComponent, null)
} }
if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
if (shapeFlag & ShapeFlags.COMPONENT) { if (shapeFlag & ShapeFlags.COMPONENT) {
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
;(parentComponent!.sink as KeepAliveSink).deactivate(vnode) ;(parentComponent!.sink as KeepAliveSink).deactivate(vnode)
} else { } else {
unmountComponent(vnode.component!, parentSuspense, doRemove) unmountComponent(vnode.component!, parentSuspense, doRemove)
} }
return } else {
}
if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
vnode.suspense!.unmount(parentSuspense, doRemove) vnode.suspense!.unmount(parentSuspense, doRemove)
return return
} }
if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
if (shouldInvokeDirs) { if (shouldInvokeDirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount') invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
} }
@ -1698,12 +1698,17 @@ function baseCreateRenderer<
// fast path for block nodes: only need to unmount dynamic children. // fast path for block nodes: only need to unmount dynamic children.
unmountChildren(dynamicChildren, parentComponent, parentSuspense) unmountChildren(dynamicChildren, parentComponent, parentSuspense)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(children as HostVNode[], parentComponent, parentSuspense) unmountChildren(
children as HostVNode[],
parentComponent,
parentSuspense
)
} }
if (doRemove) { if (doRemove) {
remove(vnode) remove(vnode)
} }
}
if ( if (
(vnodeHook = props && props.onVnodeUnmounted) != null || (vnodeHook = props && props.onVnodeUnmounted) != null ||