baseWatch.spec.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import type { Scheduler, SchedulerJob } from '../src/baseWatch'
  2. import {
  3. BaseWatchErrorCodes,
  4. EffectScope,
  5. type Ref,
  6. baseWatch,
  7. onEffectCleanup,
  8. ref,
  9. } from '../src'
  10. const queue: SchedulerJob[] = []
  11. // these codes are a simple scheduler
  12. let isFlushPending = false
  13. const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
  14. const nextTick = (fn?: () => any) =>
  15. fn ? resolvedPromise.then(fn) : resolvedPromise
  16. const scheduler: Scheduler = job => {
  17. queue.push(job)
  18. flushJobs()
  19. }
  20. const flushJobs = () => {
  21. if (isFlushPending) return
  22. isFlushPending = true
  23. resolvedPromise.then(() => {
  24. queue.forEach(job => job())
  25. queue.length = 0
  26. isFlushPending = false
  27. })
  28. }
  29. describe('baseWatch', () => {
  30. test('effect', () => {
  31. let dummy: any
  32. const source = ref(0)
  33. baseWatch(() => {
  34. dummy = source.value
  35. })
  36. expect(dummy).toBe(0)
  37. source.value++
  38. expect(dummy).toBe(1)
  39. })
  40. test('watch', () => {
  41. let dummy: any
  42. const source = ref(0)
  43. baseWatch(source, () => {
  44. dummy = source.value
  45. })
  46. expect(dummy).toBe(undefined)
  47. source.value++
  48. expect(dummy).toBe(1)
  49. })
  50. test('custom error handler', () => {
  51. const onError = vi.fn()
  52. baseWatch(
  53. () => {
  54. throw 'oops in effect'
  55. },
  56. null,
  57. { onError },
  58. )
  59. const source = ref(0)
  60. const effect = baseWatch(
  61. source,
  62. () => {
  63. onEffectCleanup(() => {
  64. throw 'oops in cleanup'
  65. })
  66. throw 'oops in watch'
  67. },
  68. { onError },
  69. )
  70. expect(onError.mock.calls.length).toBe(1)
  71. expect(onError.mock.calls[0]).toMatchObject([
  72. 'oops in effect',
  73. BaseWatchErrorCodes.WATCH_CALLBACK,
  74. ])
  75. source.value++
  76. expect(onError.mock.calls.length).toBe(2)
  77. expect(onError.mock.calls[1]).toMatchObject([
  78. 'oops in watch',
  79. BaseWatchErrorCodes.WATCH_CALLBACK,
  80. ])
  81. effect!.stop()
  82. source.value++
  83. expect(onError.mock.calls.length).toBe(3)
  84. expect(onError.mock.calls[2]).toMatchObject([
  85. 'oops in cleanup',
  86. BaseWatchErrorCodes.WATCH_CLEANUP,
  87. ])
  88. })
  89. test('baseWatch with onEffectCleanup', async () => {
  90. let dummy = 0
  91. let source: Ref<number>
  92. const scope = new EffectScope()
  93. scope.run(() => {
  94. source = ref(0)
  95. baseWatch(onCleanup => {
  96. source.value
  97. onCleanup(() => (dummy += 2))
  98. onEffectCleanup(() => (dummy += 3))
  99. onEffectCleanup(() => (dummy += 5))
  100. })
  101. })
  102. expect(dummy).toBe(0)
  103. scope.run(() => {
  104. source.value++
  105. })
  106. expect(dummy).toBe(10)
  107. scope.run(() => {
  108. source.value++
  109. })
  110. expect(dummy).toBe(20)
  111. scope.stop()
  112. expect(dummy).toBe(30)
  113. })
  114. test('nested calls to baseWatch and onEffectCleanup', async () => {
  115. let calls: string[] = []
  116. let source: Ref<number>
  117. let copyist: Ref<number>
  118. const scope = new EffectScope()
  119. scope.run(() => {
  120. source = ref(0)
  121. copyist = ref(0)
  122. // sync by default
  123. baseWatch(
  124. () => {
  125. const current = (copyist.value = source.value)
  126. onEffectCleanup(() => calls.push(`sync ${current}`))
  127. },
  128. null,
  129. {},
  130. )
  131. // with scheduler
  132. baseWatch(
  133. () => {
  134. const current = copyist.value
  135. onEffectCleanup(() => calls.push(`post ${current}`))
  136. },
  137. null,
  138. { scheduler },
  139. )
  140. })
  141. await nextTick()
  142. expect(calls).toEqual([])
  143. scope.run(() => source.value++)
  144. expect(calls).toEqual(['sync 0'])
  145. await nextTick()
  146. expect(calls).toEqual(['sync 0', 'post 0'])
  147. calls.length = 0
  148. scope.run(() => source.value++)
  149. expect(calls).toEqual(['sync 1'])
  150. await nextTick()
  151. expect(calls).toEqual(['sync 1', 'post 1'])
  152. calls.length = 0
  153. scope.stop()
  154. expect(calls).toEqual(['sync 2', 'post 2'])
  155. })
  156. })