directives.spec.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. import {
  2. h,
  3. withDirectives,
  4. ref,
  5. render,
  6. nodeOps,
  7. DirectiveHook,
  8. VNode,
  9. DirectiveBinding,
  10. nextTick
  11. } from '@vue/runtime-test'
  12. describe('directives', () => {
  13. it('should work', async () => {
  14. const count = ref(0)
  15. function assertBindings(binding: DirectiveBinding) {
  16. expect(binding.value).toBe(count.value)
  17. expect(binding.arg).toBe('foo')
  18. expect(binding.modifiers && binding.modifiers.ok).toBe(true)
  19. }
  20. const beforeMount = jest.fn(((el, binding, vnode, prevVNode) => {
  21. expect(el.tag).toBe('div')
  22. // should not be inserted yet
  23. expect(el.parentNode).toBe(null)
  24. expect(root.children.length).toBe(0)
  25. assertBindings(binding)
  26. expect(vnode).toBe(_vnode)
  27. expect(prevVNode).toBe(null)
  28. }) as DirectiveHook)
  29. const mounted = jest.fn(((el, binding, vnode, prevVNode) => {
  30. expect(el.tag).toBe('div')
  31. // should be inserted now
  32. expect(el.parentNode).toBe(root)
  33. expect(root.children[0]).toBe(el)
  34. assertBindings(binding)
  35. expect(vnode).toBe(_vnode)
  36. expect(prevVNode).toBe(null)
  37. }) as DirectiveHook)
  38. const beforeUpdate = jest.fn(((el, binding, vnode, prevVNode) => {
  39. expect(el.tag).toBe('div')
  40. expect(el.parentNode).toBe(root)
  41. expect(root.children[0]).toBe(el)
  42. // node should not have been updated yet
  43. expect(el.children[0].text).toBe(`${count.value - 1}`)
  44. assertBindings(binding)
  45. expect(vnode).toBe(_vnode)
  46. expect(prevVNode).toBe(_prevVnode)
  47. }) as DirectiveHook)
  48. const updated = jest.fn(((el, binding, vnode, prevVNode) => {
  49. expect(el.tag).toBe('div')
  50. expect(el.parentNode).toBe(root)
  51. expect(root.children[0]).toBe(el)
  52. // node should have been updated
  53. expect(el.children[0].text).toBe(`${count.value}`)
  54. assertBindings(binding)
  55. expect(vnode).toBe(_vnode)
  56. expect(prevVNode).toBe(_prevVnode)
  57. }) as DirectiveHook)
  58. const beforeUnmount = jest.fn(((el, binding, vnode, prevVNode) => {
  59. expect(el.tag).toBe('div')
  60. // should be removed now
  61. expect(el.parentNode).toBe(root)
  62. expect(root.children[0]).toBe(el)
  63. assertBindings(binding)
  64. expect(vnode).toBe(_vnode)
  65. expect(prevVNode).toBe(null)
  66. }) as DirectiveHook)
  67. const unmounted = jest.fn(((el, binding, vnode, prevVNode) => {
  68. expect(el.tag).toBe('div')
  69. // should have been removed
  70. expect(el.parentNode).toBe(null)
  71. expect(root.children.length).toBe(0)
  72. assertBindings(binding)
  73. expect(vnode).toBe(_vnode)
  74. expect(prevVNode).toBe(null)
  75. }) as DirectiveHook)
  76. const dir = {
  77. beforeMount,
  78. mounted,
  79. beforeUpdate,
  80. updated,
  81. beforeUnmount,
  82. unmounted
  83. }
  84. let _vnode: VNode | null = null
  85. let _prevVnode: VNode | null = null
  86. const Comp = {
  87. render() {
  88. _prevVnode = _vnode
  89. _vnode = withDirectives(h('div', count.value), [
  90. [
  91. dir,
  92. // value
  93. count.value,
  94. // argument
  95. 'foo',
  96. // modifiers
  97. { ok: true }
  98. ]
  99. ])
  100. return _vnode
  101. }
  102. }
  103. const root = nodeOps.createElement('div')
  104. render(h(Comp), root)
  105. expect(beforeMount).toHaveBeenCalledTimes(1)
  106. expect(mounted).toHaveBeenCalledTimes(1)
  107. count.value++
  108. await nextTick()
  109. expect(beforeUpdate).toHaveBeenCalledTimes(1)
  110. expect(updated).toHaveBeenCalledTimes(1)
  111. render(null, root)
  112. expect(beforeUnmount).toHaveBeenCalledTimes(1)
  113. expect(unmounted).toHaveBeenCalledTimes(1)
  114. })
  115. it('should work with a function directive', async () => {
  116. const count = ref(0)
  117. function assertBindings(binding: DirectiveBinding) {
  118. expect(binding.value).toBe(count.value)
  119. expect(binding.arg).toBe('foo')
  120. expect(binding.modifiers && binding.modifiers.ok).toBe(true)
  121. }
  122. const fn = jest.fn(((el, binding, vnode, prevVNode) => {
  123. expect(el.tag).toBe('div')
  124. expect(el.parentNode).toBe(root)
  125. assertBindings(binding)
  126. expect(vnode).toBe(_vnode)
  127. expect(prevVNode).toBe(_prevVnode)
  128. }) as DirectiveHook)
  129. let _vnode: VNode | null = null
  130. let _prevVnode: VNode | null = null
  131. const Comp = {
  132. render() {
  133. _prevVnode = _vnode
  134. _vnode = withDirectives(h('div', count.value), [
  135. [
  136. fn,
  137. // value
  138. count.value,
  139. // argument
  140. 'foo',
  141. // modifiers
  142. { ok: true }
  143. ]
  144. ])
  145. return _vnode
  146. }
  147. }
  148. const root = nodeOps.createElement('div')
  149. render(h(Comp), root)
  150. expect(fn).toHaveBeenCalledTimes(1)
  151. count.value++
  152. await nextTick()
  153. expect(fn).toHaveBeenCalledTimes(2)
  154. })
  155. it('should work on component vnode', async () => {
  156. const count = ref(0)
  157. function assertBindings(binding: DirectiveBinding) {
  158. expect(binding.value).toBe(count.value)
  159. expect(binding.arg).toBe('foo')
  160. expect(binding.modifiers && binding.modifiers.ok).toBe(true)
  161. }
  162. const beforeMount = jest.fn(((el, binding, vnode, prevVNode) => {
  163. expect(el.tag).toBe('div')
  164. // should not be inserted yet
  165. expect(el.parentNode).toBe(null)
  166. expect(root.children.length).toBe(0)
  167. assertBindings(binding)
  168. expect(vnode.type).toBe(_vnode!.type)
  169. expect(prevVNode).toBe(null)
  170. }) as DirectiveHook)
  171. const mounted = jest.fn(((el, binding, vnode, prevVNode) => {
  172. expect(el.tag).toBe('div')
  173. // should be inserted now
  174. expect(el.parentNode).toBe(root)
  175. expect(root.children[0]).toBe(el)
  176. assertBindings(binding)
  177. expect(vnode.type).toBe(_vnode!.type)
  178. expect(prevVNode).toBe(null)
  179. }) as DirectiveHook)
  180. const beforeUpdate = jest.fn(((el, binding, vnode, prevVNode) => {
  181. expect(el.tag).toBe('div')
  182. expect(el.parentNode).toBe(root)
  183. expect(root.children[0]).toBe(el)
  184. // node should not have been updated yet
  185. // expect(el.children[0].text).toBe(`${count.value - 1}`)
  186. assertBindings(binding)
  187. expect(vnode.type).toBe(_vnode!.type)
  188. expect(prevVNode!.type).toBe(_prevVnode!.type)
  189. }) as DirectiveHook)
  190. const updated = jest.fn(((el, binding, vnode, prevVNode) => {
  191. expect(el.tag).toBe('div')
  192. expect(el.parentNode).toBe(root)
  193. expect(root.children[0]).toBe(el)
  194. // node should have been updated
  195. expect(el.children[0].text).toBe(`${count.value}`)
  196. assertBindings(binding)
  197. expect(vnode.type).toBe(_vnode!.type)
  198. expect(prevVNode!.type).toBe(_prevVnode!.type)
  199. }) as DirectiveHook)
  200. const beforeUnmount = jest.fn(((el, binding, vnode, prevVNode) => {
  201. expect(el.tag).toBe('div')
  202. // should be removed now
  203. expect(el.parentNode).toBe(root)
  204. expect(root.children[0]).toBe(el)
  205. assertBindings(binding)
  206. expect(vnode.type).toBe(_vnode!.type)
  207. expect(prevVNode).toBe(null)
  208. }) as DirectiveHook)
  209. const unmounted = jest.fn(((el, binding, vnode, prevVNode) => {
  210. expect(el.tag).toBe('div')
  211. // should have been removed
  212. expect(el.parentNode).toBe(null)
  213. expect(root.children.length).toBe(0)
  214. assertBindings(binding)
  215. expect(vnode.type).toBe(_vnode!.type)
  216. expect(prevVNode).toBe(null)
  217. }) as DirectiveHook)
  218. const dir = {
  219. beforeMount,
  220. mounted,
  221. beforeUpdate,
  222. updated,
  223. beforeUnmount,
  224. unmounted
  225. }
  226. let _vnode: VNode | null = null
  227. let _prevVnode: VNode | null = null
  228. const Child = (props: { count: number }) => {
  229. _prevVnode = _vnode
  230. _vnode = h('div', props.count)
  231. return _vnode
  232. }
  233. const Comp = {
  234. render() {
  235. return withDirectives(h(Child, { count: count.value }), [
  236. [
  237. dir,
  238. // value
  239. count.value,
  240. // argument
  241. 'foo',
  242. // modifiers
  243. { ok: true }
  244. ]
  245. ])
  246. }
  247. }
  248. const root = nodeOps.createElement('div')
  249. render(h(Comp), root)
  250. expect(beforeMount).toHaveBeenCalledTimes(1)
  251. expect(mounted).toHaveBeenCalledTimes(1)
  252. count.value++
  253. await nextTick()
  254. expect(beforeUpdate).toHaveBeenCalledTimes(1)
  255. expect(updated).toHaveBeenCalledTimes(1)
  256. render(null, root)
  257. expect(beforeUnmount).toHaveBeenCalledTimes(1)
  258. expect(unmounted).toHaveBeenCalledTimes(1)
  259. })
  260. })