renderWatch.spec.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import {
  2. nextTick,
  3. onBeforeUpdate,
  4. onUpdated,
  5. onWatcherCleanup,
  6. ref,
  7. renderEffect,
  8. renderWatch,
  9. template,
  10. watchEffect,
  11. watchPostEffect,
  12. watchSyncEffect,
  13. } from '../src'
  14. import { makeRender } from './_utils'
  15. const define = makeRender<any>()
  16. const createDemo = (setupFn: () => any, renderFn: (ctx: any) => any) =>
  17. define({
  18. setup: () => {
  19. const returned = setupFn()
  20. Object.defineProperty(returned, '__isScriptSetup', {
  21. enumerable: false,
  22. value: true,
  23. })
  24. return returned
  25. },
  26. render: (ctx: any) => {
  27. const t0 = template('<div></div>')
  28. renderFn(ctx)
  29. return t0()
  30. },
  31. })
  32. describe('renderWatch', () => {
  33. test('effect', async () => {
  34. let dummy: any
  35. const source = ref(0)
  36. renderEffect(() => {
  37. dummy = source.value
  38. })
  39. expect(dummy).toBe(0)
  40. await nextTick()
  41. expect(dummy).toBe(0)
  42. source.value++
  43. expect(dummy).toBe(0)
  44. await nextTick()
  45. expect(dummy).toBe(1)
  46. source.value++
  47. expect(dummy).toBe(1)
  48. await nextTick()
  49. expect(dummy).toBe(2)
  50. source.value++
  51. expect(dummy).toBe(2)
  52. await nextTick()
  53. expect(dummy).toBe(3)
  54. })
  55. test('watch', async () => {
  56. let dummy: any
  57. const source = ref(0)
  58. renderWatch(source, () => {
  59. dummy = source.value
  60. })
  61. await nextTick()
  62. expect(dummy).toBe(undefined)
  63. source.value++
  64. expect(dummy).toBe(undefined)
  65. await nextTick()
  66. expect(dummy).toBe(1)
  67. source.value++
  68. expect(dummy).toBe(1)
  69. await nextTick()
  70. expect(dummy).toBe(2)
  71. })
  72. test('should run with the scheduling order', async () => {
  73. const calls: string[] = []
  74. const { instance } = createDemo(
  75. () => {
  76. // setup
  77. const source = ref(0)
  78. const renderSource = ref(0)
  79. const change = () => source.value++
  80. const changeRender = () => renderSource.value++
  81. // Life Cycle Hooks
  82. onUpdated(() => {
  83. calls.push(`updated ${source.value}`)
  84. })
  85. onBeforeUpdate(() => {
  86. calls.push(`beforeUpdate ${source.value}`)
  87. })
  88. // Watch API
  89. watchPostEffect(() => {
  90. const current = source.value
  91. calls.push(`post ${current}`)
  92. onWatcherCleanup(() => calls.push(`post cleanup ${current}`))
  93. })
  94. watchEffect(() => {
  95. const current = source.value
  96. calls.push(`pre ${current}`)
  97. onWatcherCleanup(() => calls.push(`pre cleanup ${current}`))
  98. })
  99. watchSyncEffect(() => {
  100. const current = source.value
  101. calls.push(`sync ${current}`)
  102. onWatcherCleanup(() => calls.push(`sync cleanup ${current}`))
  103. })
  104. return { source, change, renderSource, changeRender }
  105. },
  106. // render
  107. _ctx => {
  108. // Render Watch API
  109. renderEffect(() => {
  110. const current = _ctx.renderSource
  111. calls.push(`renderEffect ${current}`)
  112. onWatcherCleanup(() => calls.push(`renderEffect cleanup ${current}`))
  113. })
  114. renderWatch(
  115. () => _ctx.renderSource,
  116. value => {
  117. calls.push(`renderWatch ${value}`)
  118. onWatcherCleanup(() => calls.push(`renderWatch cleanup ${value}`))
  119. },
  120. )
  121. },
  122. ).render()
  123. const { change, changeRender } = instance.setupState as any
  124. expect(calls).toEqual(['pre 0', 'sync 0', 'renderEffect 0', 'post 0'])
  125. calls.length = 0
  126. // Update
  127. changeRender()
  128. change()
  129. expect(calls).toEqual(['sync cleanup 0', 'sync 1'])
  130. calls.length = 0
  131. await nextTick()
  132. expect(calls).toEqual([
  133. 'pre cleanup 0',
  134. 'pre 1',
  135. 'beforeUpdate 1',
  136. 'renderEffect cleanup 0',
  137. 'renderEffect 1',
  138. 'renderWatch 1',
  139. 'post cleanup 0',
  140. 'post 1',
  141. 'updated 1',
  142. ])
  143. calls.length = 0
  144. // Update
  145. changeRender()
  146. change()
  147. expect(calls).toEqual(['sync cleanup 1', 'sync 2'])
  148. calls.length = 0
  149. await nextTick()
  150. expect(calls).toEqual([
  151. 'pre cleanup 1',
  152. 'pre 2',
  153. 'beforeUpdate 2',
  154. 'renderEffect cleanup 1',
  155. 'renderEffect 2',
  156. 'renderWatch cleanup 1',
  157. 'renderWatch 2',
  158. 'post cleanup 1',
  159. 'post 2',
  160. 'updated 2',
  161. ])
  162. })
  163. test('errors should include the execution location with beforeUpdate hook', async () => {
  164. const { instance } = createDemo(
  165. // setup
  166. () => {
  167. const source = ref()
  168. const update = () => source.value++
  169. onBeforeUpdate(() => {
  170. throw 'error in beforeUpdate'
  171. })
  172. return { source, update }
  173. },
  174. // render
  175. ctx => {
  176. renderEffect(() => {
  177. ctx.source
  178. })
  179. },
  180. ).render()
  181. const { update } = instance.setupState as any
  182. await expect(async () => {
  183. update()
  184. await nextTick()
  185. }).rejects.toThrow('error in beforeUpdate')
  186. expect(
  187. '[Vue warn] Unhandled error during execution of beforeUpdate hook',
  188. ).toHaveBeenWarned()
  189. })
  190. test('errors should include the execution location with updated hook', async () => {
  191. const { instance } = createDemo(
  192. // setup
  193. () => {
  194. const source = ref(0)
  195. const update = () => source.value++
  196. onUpdated(() => {
  197. throw 'error in updated'
  198. })
  199. return { source, update }
  200. },
  201. // render
  202. ctx => {
  203. renderEffect(() => {
  204. ctx.source
  205. })
  206. },
  207. ).render()
  208. const { update } = instance.setupState as any
  209. await expect(async () => {
  210. update()
  211. await nextTick()
  212. }).rejects.toThrow('error in updated')
  213. expect(
  214. '[Vue warn] Unhandled error during execution of updated',
  215. ).toHaveBeenWarned()
  216. })
  217. })