52cc7e8231
BREAKING CHANGE: custom directive bindings no longer expose instance This is a rarely used property that creates extra complexity in ensuring it points to the correct instance. From a design perspective, a custom directive should be scoped to the element and data it is bound to and should not have access to the entire instance in the first place.
328 lines
8.3 KiB
TypeScript
328 lines
8.3 KiB
TypeScript
import {
|
|
h,
|
|
withDirectives,
|
|
ref,
|
|
render,
|
|
nodeOps,
|
|
DirectiveHook,
|
|
VNode,
|
|
DirectiveBinding,
|
|
nextTick
|
|
} from '@vue/runtime-test'
|
|
|
|
describe('directives', () => {
|
|
it('should work', async () => {
|
|
const count = ref(0)
|
|
|
|
function assertBindings(binding: DirectiveBinding) {
|
|
expect(binding.value).toBe(count.value)
|
|
expect(binding.arg).toBe('foo')
|
|
expect(binding.modifiers && binding.modifiers.ok).toBe(true)
|
|
}
|
|
|
|
const beforeMount = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should not be inserted yet
|
|
expect(el.parentNode).toBe(null)
|
|
expect(root.children.length).toBe(0)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const mounted = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should be inserted now
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const beforeUpdate = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
// node should not have been updated yet
|
|
expect(el.children[0].text).toBe(`${count.value - 1}`)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(_prevVnode)
|
|
}) as DirectiveHook)
|
|
|
|
const updated = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
// node should have been updated
|
|
expect(el.children[0].text).toBe(`${count.value}`)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(_prevVnode)
|
|
}) as DirectiveHook)
|
|
|
|
const beforeUnmount = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should be removed now
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const unmounted = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should have been removed
|
|
expect(el.parentNode).toBe(null)
|
|
expect(root.children.length).toBe(0)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const dir = {
|
|
beforeMount,
|
|
mounted,
|
|
beforeUpdate,
|
|
updated,
|
|
beforeUnmount,
|
|
unmounted
|
|
}
|
|
|
|
let _vnode: VNode | null = null
|
|
let _prevVnode: VNode | null = null
|
|
const Comp = {
|
|
render() {
|
|
_prevVnode = _vnode
|
|
_vnode = withDirectives(h('div', count.value), [
|
|
[
|
|
dir,
|
|
// value
|
|
count.value,
|
|
// argument
|
|
'foo',
|
|
// modifiers
|
|
{ ok: true }
|
|
]
|
|
])
|
|
return _vnode
|
|
}
|
|
}
|
|
|
|
const root = nodeOps.createElement('div')
|
|
render(h(Comp), root)
|
|
|
|
expect(beforeMount).toHaveBeenCalledTimes(1)
|
|
expect(mounted).toHaveBeenCalledTimes(1)
|
|
|
|
count.value++
|
|
await nextTick()
|
|
expect(beforeUpdate).toHaveBeenCalledTimes(1)
|
|
expect(updated).toHaveBeenCalledTimes(1)
|
|
|
|
render(null, root)
|
|
expect(beforeUnmount).toHaveBeenCalledTimes(1)
|
|
expect(unmounted).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('should work with a function directive', async () => {
|
|
const count = ref(0)
|
|
|
|
function assertBindings(binding: DirectiveBinding) {
|
|
expect(binding.value).toBe(count.value)
|
|
expect(binding.arg).toBe('foo')
|
|
expect(binding.modifiers && binding.modifiers.ok).toBe(true)
|
|
}
|
|
|
|
const fn = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
expect(el.parentNode).toBe(root)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode).toBe(_vnode)
|
|
expect(prevVNode).toBe(_prevVnode)
|
|
}) as DirectiveHook)
|
|
|
|
let _vnode: VNode | null = null
|
|
let _prevVnode: VNode | null = null
|
|
const Comp = {
|
|
render() {
|
|
_prevVnode = _vnode
|
|
_vnode = withDirectives(h('div', count.value), [
|
|
[
|
|
fn,
|
|
// value
|
|
count.value,
|
|
// argument
|
|
'foo',
|
|
// modifiers
|
|
{ ok: true }
|
|
]
|
|
])
|
|
return _vnode
|
|
}
|
|
}
|
|
|
|
const root = nodeOps.createElement('div')
|
|
render(h(Comp), root)
|
|
|
|
expect(fn).toHaveBeenCalledTimes(1)
|
|
|
|
count.value++
|
|
await nextTick()
|
|
expect(fn).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
it('should work on component vnode', async () => {
|
|
const count = ref(0)
|
|
|
|
function assertBindings(binding: DirectiveBinding) {
|
|
expect(binding.value).toBe(count.value)
|
|
expect(binding.arg).toBe('foo')
|
|
expect(binding.modifiers && binding.modifiers.ok).toBe(true)
|
|
}
|
|
|
|
const beforeMount = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should not be inserted yet
|
|
expect(el.parentNode).toBe(null)
|
|
expect(root.children.length).toBe(0)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode.type).toBe(_vnode!.type)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const mounted = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should be inserted now
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode.type).toBe(_vnode!.type)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const beforeUpdate = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
// node should not have been updated yet
|
|
// expect(el.children[0].text).toBe(`${count.value - 1}`)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode.type).toBe(_vnode!.type)
|
|
expect(prevVNode!.type).toBe(_prevVnode!.type)
|
|
}) as DirectiveHook)
|
|
|
|
const updated = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
// node should have been updated
|
|
expect(el.children[0].text).toBe(`${count.value}`)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode.type).toBe(_vnode!.type)
|
|
expect(prevVNode!.type).toBe(_prevVnode!.type)
|
|
}) as DirectiveHook)
|
|
|
|
const beforeUnmount = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should be removed now
|
|
expect(el.parentNode).toBe(root)
|
|
expect(root.children[0]).toBe(el)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode.type).toBe(_vnode!.type)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const unmounted = jest.fn(((el, binding, vnode, prevVNode) => {
|
|
expect(el.tag).toBe('div')
|
|
// should have been removed
|
|
expect(el.parentNode).toBe(null)
|
|
expect(root.children.length).toBe(0)
|
|
|
|
assertBindings(binding)
|
|
|
|
expect(vnode.type).toBe(_vnode!.type)
|
|
expect(prevVNode).toBe(null)
|
|
}) as DirectiveHook)
|
|
|
|
const dir = {
|
|
beforeMount,
|
|
mounted,
|
|
beforeUpdate,
|
|
updated,
|
|
beforeUnmount,
|
|
unmounted
|
|
}
|
|
|
|
let _vnode: VNode | null = null
|
|
let _prevVnode: VNode | null = null
|
|
|
|
const Child = (props: { count: number }) => {
|
|
_prevVnode = _vnode
|
|
_vnode = h('div', props.count)
|
|
return _vnode
|
|
}
|
|
|
|
const Comp = {
|
|
render() {
|
|
return withDirectives(h(Child, { count: count.value }), [
|
|
[
|
|
dir,
|
|
// value
|
|
count.value,
|
|
// argument
|
|
'foo',
|
|
// modifiers
|
|
{ ok: true }
|
|
]
|
|
])
|
|
}
|
|
}
|
|
|
|
const root = nodeOps.createElement('div')
|
|
render(h(Comp), root)
|
|
|
|
expect(beforeMount).toHaveBeenCalledTimes(1)
|
|
expect(mounted).toHaveBeenCalledTimes(1)
|
|
|
|
count.value++
|
|
await nextTick()
|
|
expect(beforeUpdate).toHaveBeenCalledTimes(1)
|
|
expect(updated).toHaveBeenCalledTimes(1)
|
|
|
|
render(null, root)
|
|
expect(beforeUnmount).toHaveBeenCalledTimes(1)
|
|
expect(unmounted).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|