apiWatch.spec.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { EffectScope, Ref, ref } from '@vue/reactivity'
  2. import {
  3. onEffectCleanup,
  4. watchEffect,
  5. watchPostEffect,
  6. watchSyncEffect,
  7. } from '../src/apiWatch'
  8. import { nextTick } from '../src/scheduler'
  9. import { defineComponent } from 'vue'
  10. import { render } from '../src/render'
  11. import { template } from '../src/template'
  12. let host: HTMLElement
  13. const initHost = () => {
  14. host = document.createElement('div')
  15. host.setAttribute('id', 'host')
  16. document.body.appendChild(host)
  17. }
  18. beforeEach(() => {
  19. initHost()
  20. })
  21. afterEach(() => {
  22. host.remove()
  23. })
  24. describe('watchEffect and onEffectCleanup', () => {
  25. test('basic', async () => {
  26. let dummy = 0
  27. let source: Ref<number>
  28. const scope = new EffectScope()
  29. scope.run(() => {
  30. source = ref(0)
  31. watchEffect((onCleanup) => {
  32. source.value
  33. onCleanup(() => (dummy += 2))
  34. onEffectCleanup(() => (dummy += 3))
  35. onEffectCleanup(() => (dummy += 5))
  36. })
  37. })
  38. await nextTick()
  39. expect(dummy).toBe(0)
  40. scope.run(() => {
  41. source.value++
  42. })
  43. await nextTick()
  44. expect(dummy).toBe(10)
  45. scope.run(() => {
  46. source.value++
  47. })
  48. await nextTick()
  49. expect(dummy).toBe(20)
  50. scope.stop()
  51. await nextTick()
  52. expect(dummy).toBe(30)
  53. })
  54. test('nested call to watchEffect', async () => {
  55. let dummy = 0
  56. let source: Ref<number>
  57. let double: Ref<number>
  58. const scope = new EffectScope()
  59. scope.run(() => {
  60. source = ref(0)
  61. double = ref(0)
  62. watchEffect(() => {
  63. double.value = source.value * 2
  64. onEffectCleanup(() => (dummy += 2))
  65. })
  66. watchSyncEffect(() => {
  67. double.value
  68. onEffectCleanup(() => (dummy += 3))
  69. })
  70. })
  71. await nextTick()
  72. expect(dummy).toBe(0)
  73. scope.run(() => source.value++)
  74. await nextTick()
  75. expect(dummy).toBe(5)
  76. scope.run(() => source.value++)
  77. await nextTick()
  78. expect(dummy).toBe(10)
  79. scope.stop()
  80. await nextTick()
  81. expect(dummy).toBe(15)
  82. })
  83. test('scheduling order', async () => {
  84. const calls: string[] = []
  85. const demo = defineComponent({
  86. setup() {
  87. const source = ref(0)
  88. const change = () => source.value++
  89. watchPostEffect(() => {
  90. const current = source.value
  91. calls.push(`post ${current}`)
  92. onEffectCleanup(() => calls.push(`post cleanup ${current}`))
  93. })
  94. watchEffect(() => {
  95. const current = source.value
  96. calls.push(`pre ${current}`)
  97. onEffectCleanup(() => calls.push(`pre cleanup ${current}`))
  98. })
  99. watchSyncEffect(() => {
  100. const current = source.value
  101. calls.push(`sync ${current}`)
  102. onEffectCleanup(() => calls.push(`sync cleanup ${current}`))
  103. })
  104. const __returned__ = { source, change }
  105. Object.defineProperty(__returned__, '__isScriptSetup', {
  106. enumerable: false,
  107. value: true,
  108. })
  109. return __returned__
  110. },
  111. })
  112. demo.render = (_ctx: any) => {
  113. const t0 = template('<div></div>')
  114. watchEffect(() => {
  115. const current = _ctx.source
  116. calls.push(`render ${current}`)
  117. onEffectCleanup(() => calls.push(`render cleanup ${current}`))
  118. })
  119. return t0()
  120. }
  121. const instance = render(demo as any, {}, '#host')
  122. const { change } = instance.setupState as any
  123. expect(calls).toEqual(['pre 0', 'sync 0', 'render 0'])
  124. calls.length = 0
  125. await nextTick()
  126. expect(calls).toEqual(['post 0'])
  127. calls.length = 0
  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. 'render cleanup 0',
  136. 'render 1',
  137. 'post cleanup 0',
  138. 'post 1',
  139. ])
  140. })
  141. })