You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
10 KiB
321 lines
10 KiB
import { patch } from 'web/runtime/patch'
|
|
import { createPatchFunction } from 'core/vdom/patch'
|
|
import baseModules from 'core/vdom/modules/index'
|
|
import * as nodeOps from 'web/runtime/node-ops'
|
|
import platformModules from 'web/runtime/modules/index'
|
|
import VNode from 'core/vdom/vnode'
|
|
|
|
const modules = baseModules.concat(platformModules)
|
|
|
|
describe('hooks', () => {
|
|
let vnode0
|
|
beforeEach(() => {
|
|
vnode0 = new VNode('p', { attrs: { id: '1' }}, [createTextVNode('hello world')])
|
|
patch(null, vnode0)
|
|
})
|
|
|
|
it('should call `insert` listener after both parents, siblings and children have been inserted', () => {
|
|
const result = []
|
|
function insert (vnode) {
|
|
expect(vnode.elm.children.length).toBe(2)
|
|
expect(vnode.elm.parentNode.children.length).toBe(3)
|
|
result.push(vnode)
|
|
}
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { insert }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
]),
|
|
new VNode('span', {}, undefined, 'can touch me')
|
|
])
|
|
patch(vnode0, vnode1)
|
|
expect(result.length).toBe(1)
|
|
})
|
|
|
|
it('should call `prepatch` listener', () => {
|
|
const result = []
|
|
function prepatch (oldVnode, newVnode) {
|
|
expect(oldVnode).toEqual(vnode1.children[1])
|
|
expect(newVnode).toEqual(vnode2.children[1])
|
|
result.push(newVnode)
|
|
}
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { prepatch }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { prepatch }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
expect(result.length).toBe(1)
|
|
})
|
|
|
|
it('should call `postpatch` after `prepatch` listener', () => {
|
|
const pre = []
|
|
const post = []
|
|
function prepatch (oldVnode, newVnode) {
|
|
pre.push(pre)
|
|
}
|
|
function postpatch (oldVnode, newVnode) {
|
|
expect(pre.length).toBe(post.length + 1)
|
|
post.push(post)
|
|
}
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { prepatch, postpatch }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { prepatch, postpatch }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
expect(pre.length).toBe(1)
|
|
expect(post.length).toBe(1)
|
|
})
|
|
|
|
it('should call `update` listener', () => {
|
|
const result1 = []
|
|
const result2 = []
|
|
function cb (result, oldVnode, newVnode) {
|
|
if (result.length > 1) {
|
|
expect(result[result.length - 1]).toEqual(oldVnode)
|
|
}
|
|
result.push(newVnode)
|
|
}
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { update: cb.bind(null, result1) }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', { hook: { update: cb.bind(null, result2) }}, undefined, 'child 2')
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { update: cb.bind(null, result1) }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', { hook: { update: cb.bind(null, result2) }}, undefined, 'child 2')
|
|
])
|
|
])
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
expect(result1.length).toBe(1)
|
|
expect(result2.length).toBe(1)
|
|
})
|
|
|
|
it('should call `remove` listener', () => {
|
|
const result = []
|
|
function remove (vnode, rm) {
|
|
const parent = vnode.elm.parentNode
|
|
expect(vnode.elm.children.length).toBe(2)
|
|
expect(vnode.elm.children.length).toBe(2)
|
|
result.push(vnode)
|
|
rm()
|
|
expect(parent.children.length).toBe(1)
|
|
}
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { remove }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling')
|
|
])
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
expect(result.length).toBe(1)
|
|
})
|
|
|
|
it('should call `init` and `prepatch` listeners on root', () => {
|
|
let count = 0
|
|
function init (vnode) { count++ }
|
|
function prepatch (oldVnode, newVnode) { count++ }
|
|
const vnode1 = new VNode('div', { hook: { init, prepatch }})
|
|
patch(vnode0, vnode1)
|
|
expect(count).toBe(1)
|
|
const vnode2 = new VNode('span', { hook: { init, prepatch }})
|
|
patch(vnode1, vnode2)
|
|
expect(count).toBe(2)
|
|
})
|
|
|
|
it('should remove element when all remove listeners are done', () => {
|
|
let rm1, rm2, rm3
|
|
const patch1 = createPatchFunction({
|
|
nodeOps,
|
|
modules: modules.concat([
|
|
{ remove (_, rm) { rm1 = rm } },
|
|
{ remove (_, rm) { rm2 = rm } }
|
|
])
|
|
})
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('a', { hook: { remove (_, rm) { rm3 = rm } }})
|
|
])
|
|
const vnode2 = new VNode('div', {}, [])
|
|
let elm = patch1(vnode0, vnode1)
|
|
expect(elm.children.length).toBe(1)
|
|
elm = patch1(vnode1, vnode2)
|
|
expect(elm.children.length).toBe(1)
|
|
rm1()
|
|
expect(elm.children.length).toBe(1)
|
|
rm3()
|
|
expect(elm.children.length).toBe(1)
|
|
rm2()
|
|
expect(elm.children.length).toBe(0)
|
|
})
|
|
|
|
it('should invoke the remove hook on replaced root', () => {
|
|
const result = []
|
|
const parent = nodeOps.createElement('div')
|
|
vnode0 = nodeOps.createElement('div')
|
|
parent.appendChild(vnode0)
|
|
function remove (vnode, rm) {
|
|
result.push(vnode)
|
|
rm()
|
|
}
|
|
const vnode1 = new VNode('div', { hook: { remove }}, [
|
|
new VNode('b', {}, undefined, 'child 1'),
|
|
new VNode('i', {}, undefined, 'child 2')
|
|
])
|
|
const vnode2 = new VNode('span', {}, [
|
|
new VNode('b', {}, undefined, 'child 1'),
|
|
new VNode('i', {}, undefined, 'child 2')
|
|
])
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
expect(result.length).toBe(1)
|
|
})
|
|
|
|
it('should invoke global `destroy` hook for all removed children', () => {
|
|
const result = []
|
|
function destroy (vnode) { result.push(vnode) }
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', {}, [
|
|
new VNode('span', { hook: { destroy }}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div')
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
expect(result.length).toBe(1)
|
|
})
|
|
|
|
it('should handle text vnodes with `undefined` `data` property', () => {
|
|
const vnode1 = new VNode('div', {}, [createTextVNode(' ')])
|
|
const vnode2 = new VNode('div', {}, [])
|
|
patch(vnode0, vnode1)
|
|
patch(vnode1, vnode2)
|
|
})
|
|
|
|
it('should invoke `destroy` module hook for all removed children', () => {
|
|
let created = 0
|
|
let destroyed = 0
|
|
const patch1 = createPatchFunction({
|
|
nodeOps,
|
|
modules: modules.concat([
|
|
{ create () { created++ } },
|
|
{ destroy () { destroyed++ } }
|
|
])
|
|
})
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div', {})
|
|
patch1(vnode0, vnode1)
|
|
expect(destroyed).toBe(1) // should invoke for replaced root nodes too
|
|
patch1(vnode1, vnode2)
|
|
expect(created).toBe(5)
|
|
expect(destroyed).toBe(5)
|
|
})
|
|
|
|
it('should not invoke `create` and `remove` module hook for text nodes', () => {
|
|
let created = 0
|
|
let removed = 0
|
|
const patch1 = createPatchFunction({
|
|
nodeOps,
|
|
modules: modules.concat([
|
|
{ create () { created++ } },
|
|
{ remove () { removed++ } }
|
|
])
|
|
})
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first child'),
|
|
createTextVNode(''),
|
|
new VNode('span', {}, undefined, 'third child')
|
|
])
|
|
const vnode2 = new VNode('div', {})
|
|
patch1(vnode0, vnode1)
|
|
patch1(vnode1, vnode2)
|
|
expect(created).toBe(3)
|
|
expect(removed).toBe(2)
|
|
})
|
|
|
|
it('should not invoke `destroy` module hook for text nodes', () => {
|
|
let created = 0
|
|
let destroyed = 0
|
|
const patch1 = createPatchFunction({
|
|
nodeOps,
|
|
modules: modules.concat([
|
|
{ create () { created++ } },
|
|
{ destroy () { destroyed++ } }
|
|
])
|
|
})
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, [
|
|
createTextVNode('text1'),
|
|
createTextVNode('text2')
|
|
])
|
|
])
|
|
])
|
|
const vnode2 = new VNode('div', {})
|
|
patch1(vnode0, vnode1)
|
|
expect(destroyed).toBe(1) // should invoke for replaced root nodes too
|
|
patch1(vnode1, vnode2)
|
|
expect(created).toBe(5)
|
|
expect(destroyed).toBe(5)
|
|
})
|
|
|
|
it('should call `create` listener before inserted into parent but after children', () => {
|
|
const result = []
|
|
function create (empty, vnode) {
|
|
expect(vnode.elm.children.length).toBe(2)
|
|
expect(vnode.elm.parentNode).toBe(null)
|
|
result.push(vnode)
|
|
}
|
|
const vnode1 = new VNode('div', {}, [
|
|
new VNode('span', {}, undefined, 'first sibling'),
|
|
new VNode('div', { hook: { create }}, [
|
|
new VNode('span', {}, undefined, 'child 1'),
|
|
new VNode('span', {}, undefined, 'child 2')
|
|
]),
|
|
new VNode('span', {}, undefined, 'can\'t touch me')
|
|
])
|
|
patch(vnode0, vnode1)
|
|
expect(result.length).toBe(1)
|
|
})
|
|
})
|
|
|