if.spec.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import {
  2. child,
  3. createIf,
  4. insert,
  5. renderEffect,
  6. template,
  7. // @ts-expect-error
  8. withDirectives,
  9. } from '../src'
  10. import { nextTick, ref } from '@vue/runtime-dom'
  11. import type { Mock } from 'vitest'
  12. import { makeRender } from './_utils'
  13. import { unmountComponent } from '../src/component'
  14. import { setElementText } from '../src/dom/prop'
  15. const define = makeRender()
  16. describe('createIf', () => {
  17. test('basic', async () => {
  18. // mock this template:
  19. // <div>
  20. // <p v-if="counter">{{counter}}</p>
  21. // <p v-else>zero</p>
  22. // </div>
  23. let spyIfFn: Mock<any>
  24. let spyElseFn: Mock<any>
  25. const count = ref(0)
  26. const spyConditionFn = vi.fn(() => count.value)
  27. // templates can be reused through caching.
  28. const t0 = template('<div></div>')
  29. const t1 = template('<p></p>')
  30. const t2 = template('<p>zero</p>')
  31. const { host } = define(() => {
  32. const n0 = t0()
  33. insert(
  34. createIf(
  35. spyConditionFn,
  36. // v-if
  37. (spyIfFn ||= vi.fn(() => {
  38. const n2 = t1()
  39. renderEffect(() => {
  40. setElementText(n2, count.value)
  41. })
  42. return n2
  43. })),
  44. // v-else
  45. (spyElseFn ||= vi.fn(() => {
  46. const n4 = t2()
  47. return n4
  48. })),
  49. ),
  50. n0 as any as ParentNode,
  51. )
  52. return n0
  53. }).render()
  54. expect(host.innerHTML).toBe('<div><p>zero</p><!--if--></div>')
  55. expect(spyConditionFn).toHaveBeenCalledTimes(1)
  56. expect(spyIfFn!).toHaveBeenCalledTimes(0)
  57. expect(spyElseFn!).toHaveBeenCalledTimes(1)
  58. count.value++
  59. await nextTick()
  60. expect(host.innerHTML).toBe('<div><p>1</p><!--if--></div>')
  61. expect(spyConditionFn).toHaveBeenCalledTimes(2)
  62. expect(spyIfFn!).toHaveBeenCalledTimes(1)
  63. expect(spyElseFn!).toHaveBeenCalledTimes(1)
  64. count.value++
  65. await nextTick()
  66. expect(host.innerHTML).toBe('<div><p>2</p><!--if--></div>')
  67. expect(spyConditionFn).toHaveBeenCalledTimes(3)
  68. expect(spyIfFn!).toHaveBeenCalledTimes(1)
  69. expect(spyElseFn!).toHaveBeenCalledTimes(1)
  70. count.value = 0
  71. await nextTick()
  72. expect(host.innerHTML).toBe('<div><p>zero</p><!--if--></div>')
  73. expect(spyConditionFn).toHaveBeenCalledTimes(4)
  74. expect(spyIfFn!).toHaveBeenCalledTimes(1)
  75. expect(spyElseFn!).toHaveBeenCalledTimes(2)
  76. })
  77. test('should handle nested template', async () => {
  78. // mock this template:
  79. // <template v-if="ok1">
  80. // Hello <template v-if="ok2">Vapor</template>
  81. // </template>
  82. const ok1 = ref(true)
  83. const ok2 = ref(true)
  84. const t0 = template('Vapor')
  85. const t1 = template('Hello ')
  86. const { host } = define(() => {
  87. const n1 = createIf(
  88. () => ok1.value,
  89. () => {
  90. const n2 = t1()
  91. const n3 = createIf(
  92. () => ok2.value,
  93. () => {
  94. const n4 = t0()
  95. return n4
  96. },
  97. )
  98. return [n2, n3]
  99. },
  100. )
  101. return n1
  102. }).render()
  103. expect(host.innerHTML).toBe('Hello Vapor<!--if--><!--if-->')
  104. ok1.value = false
  105. await nextTick()
  106. expect(host.innerHTML).toBe('<!--if-->')
  107. ok1.value = true
  108. await nextTick()
  109. expect(host.innerHTML).toBe('Hello Vapor<!--if--><!--if-->')
  110. ok2.value = false
  111. await nextTick()
  112. expect(host.innerHTML).toBe('Hello <!--if--><!--if-->')
  113. ok1.value = false
  114. await nextTick()
  115. expect(host.innerHTML).toBe('<!--if-->')
  116. })
  117. test.todo('should work with directive hooks', async () => {
  118. const calls: string[] = []
  119. const show1 = ref(true)
  120. const show2 = ref(true)
  121. const update = ref(0)
  122. const spyConditionFn1 = vi.fn(() => show1.value)
  123. const spyConditionFn2 = vi.fn(() => show2.value)
  124. const vDirective: any = {
  125. created: (el: any, { value }: any) => calls.push(`${value} created`),
  126. beforeMount: (el: any, { value }: any) =>
  127. calls.push(`${value} beforeMount`),
  128. mounted: (el: any, { value }: any) => calls.push(`${value} mounted`),
  129. beforeUpdate: (el: any, { value }: any) =>
  130. calls.push(`${value} beforeUpdate`),
  131. updated: (el: any, { value }: any) => calls.push(`${value} updated`),
  132. beforeUnmount: (el: any, { value }: any) =>
  133. calls.push(`${value} beforeUnmount`),
  134. unmounted: (el: any, { value }: any) => calls.push(`${value} unmounted`),
  135. }
  136. const t0 = template('<p></p>')
  137. const { instance } = define(() => {
  138. const n1 = createIf(
  139. spyConditionFn1,
  140. () => {
  141. const n2 = t0() as ParentNode
  142. withDirectives(child(n2), [[vDirective, () => (update.value, '1')]])
  143. return n2
  144. },
  145. () =>
  146. createIf(
  147. spyConditionFn2,
  148. () => {
  149. const n2 = t0() as ParentNode
  150. withDirectives(child(n2), [[vDirective, () => '2']])
  151. return n2
  152. },
  153. () => {
  154. const n2 = t0() as ParentNode
  155. withDirectives(child(n2), [[vDirective, () => '3']])
  156. return n2
  157. },
  158. ),
  159. )
  160. return [n1]
  161. }).render()
  162. await nextTick()
  163. expect(calls).toEqual(['1 created', '1 beforeMount', '1 mounted'])
  164. calls.length = 0
  165. expect(spyConditionFn1).toHaveBeenCalledTimes(1)
  166. expect(spyConditionFn2).toHaveBeenCalledTimes(0)
  167. show1.value = false
  168. await nextTick()
  169. expect(calls).toEqual([
  170. '1 beforeUnmount',
  171. '2 created',
  172. '2 beforeMount',
  173. '1 unmounted',
  174. '2 mounted',
  175. ])
  176. calls.length = 0
  177. expect(spyConditionFn1).toHaveBeenCalledTimes(2)
  178. expect(spyConditionFn2).toHaveBeenCalledTimes(1)
  179. show2.value = false
  180. await nextTick()
  181. expect(calls).toEqual([
  182. '2 beforeUnmount',
  183. '3 created',
  184. '3 beforeMount',
  185. '2 unmounted',
  186. '3 mounted',
  187. ])
  188. calls.length = 0
  189. expect(spyConditionFn1).toHaveBeenCalledTimes(2)
  190. expect(spyConditionFn2).toHaveBeenCalledTimes(2)
  191. show1.value = true
  192. await nextTick()
  193. expect(calls).toEqual([
  194. '3 beforeUnmount',
  195. '1 created',
  196. '1 beforeMount',
  197. '3 unmounted',
  198. '1 mounted',
  199. ])
  200. calls.length = 0
  201. expect(spyConditionFn1).toHaveBeenCalledTimes(3)
  202. expect(spyConditionFn2).toHaveBeenCalledTimes(2)
  203. update.value++
  204. await nextTick()
  205. expect(calls).toEqual(['1 beforeUpdate', '1 updated'])
  206. calls.length = 0
  207. expect(spyConditionFn1).toHaveBeenCalledTimes(3)
  208. expect(spyConditionFn2).toHaveBeenCalledTimes(2)
  209. unmountComponent(instance!)
  210. expect(calls).toEqual(['1 beforeUnmount', '1 unmounted'])
  211. expect(spyConditionFn1).toHaveBeenCalledTimes(3)
  212. expect(spyConditionFn2).toHaveBeenCalledTimes(2)
  213. })
  214. })