scheduler.spec.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import Vue from 'vue'
  2. import {
  3. MAX_UPDATE_COUNT,
  4. queueWatcher as _queueWatcher
  5. } from 'core/observer/scheduler'
  6. function queueWatcher(watcher) {
  7. watcher.vm = {} // mock vm
  8. _queueWatcher(watcher)
  9. }
  10. describe('Scheduler', () => {
  11. let spy
  12. beforeEach(() => {
  13. spy = vi.fn()
  14. })
  15. it('queueWatcher', done => {
  16. queueWatcher({
  17. run: spy
  18. })
  19. waitForUpdate(() => {
  20. expect(spy.mock.calls.length).toBe(1)
  21. }).then(done)
  22. })
  23. it('dedup', done => {
  24. queueWatcher({
  25. id: 1,
  26. run: spy
  27. })
  28. queueWatcher({
  29. id: 1,
  30. run: spy
  31. })
  32. waitForUpdate(() => {
  33. expect(spy.mock.calls.length).toBe(1)
  34. }).then(done)
  35. })
  36. it('allow duplicate when flushing', done => {
  37. const job = {
  38. id: 1,
  39. run: spy
  40. }
  41. queueWatcher(job)
  42. queueWatcher({
  43. id: 2,
  44. run() {
  45. queueWatcher(job)
  46. }
  47. })
  48. waitForUpdate(() => {
  49. expect(spy.mock.calls.length).toBe(2)
  50. }).then(done)
  51. })
  52. it('call user watchers before component re-render', done => {
  53. const calls: any[] = []
  54. const vm = new Vue({
  55. data: {
  56. a: 1
  57. },
  58. template: '<div>{{ a }}</div>',
  59. watch: {
  60. a() {
  61. calls.push(1)
  62. }
  63. },
  64. beforeUpdate() {
  65. calls.push(2)
  66. }
  67. }).$mount()
  68. vm.a = 2
  69. waitForUpdate(() => {
  70. expect(calls).toEqual([1, 2])
  71. }).then(done)
  72. })
  73. it('call user watcher triggered by component re-render immediately', done => {
  74. // this happens when a component re-render updates the props of a child
  75. const calls: any[] = []
  76. const vm = new Vue({
  77. data: {
  78. a: 1
  79. },
  80. watch: {
  81. a() {
  82. calls.push(1)
  83. }
  84. },
  85. beforeUpdate() {
  86. calls.push(2)
  87. },
  88. template: '<div><test :a="a"></test></div>',
  89. components: {
  90. test: {
  91. props: ['a'],
  92. template: '<div>{{ a }}</div>',
  93. watch: {
  94. a() {
  95. calls.push(3)
  96. }
  97. },
  98. beforeUpdate() {
  99. calls.push(4)
  100. }
  101. }
  102. }
  103. }).$mount()
  104. vm.a = 2
  105. waitForUpdate(() => {
  106. expect(calls).toEqual([1, 2, 3, 4])
  107. }).then(done)
  108. })
  109. it('warn against infinite update loops', function (done) {
  110. let count = 0
  111. const job = {
  112. id: 1,
  113. run() {
  114. count++
  115. queueWatcher(job)
  116. }
  117. }
  118. queueWatcher(job)
  119. waitForUpdate(() => {
  120. expect(count).toBe(MAX_UPDATE_COUNT + 1)
  121. expect('infinite update loop').toHaveBeenWarned()
  122. }).then(done)
  123. })
  124. it('should call newly pushed watcher after current watcher is done', done => {
  125. const callOrder: any[] = []
  126. queueWatcher({
  127. id: 1,
  128. user: true,
  129. run() {
  130. callOrder.push(1)
  131. queueWatcher({
  132. id: 2,
  133. run() {
  134. callOrder.push(3)
  135. }
  136. })
  137. callOrder.push(2)
  138. }
  139. })
  140. waitForUpdate(() => {
  141. expect(callOrder).toEqual([1, 2, 3])
  142. }).then(done)
  143. })
  144. // GitHub issue #5191
  145. it('emit should work when updated hook called', done => {
  146. const el = document.createElement('div')
  147. const vm = new Vue({
  148. template: `<div><child @change="bar" :foo="foo"></child></div>`,
  149. data: {
  150. foo: 0
  151. },
  152. methods: {
  153. bar: spy
  154. },
  155. components: {
  156. child: {
  157. template: `<div>{{foo}}</div>`,
  158. props: ['foo'],
  159. updated() {
  160. this.$emit('change')
  161. }
  162. }
  163. }
  164. }).$mount(el)
  165. vm.$nextTick(() => {
  166. vm.foo = 1
  167. vm.$nextTick(() => {
  168. expect(vm.$el.innerHTML).toBe('<div>1</div>')
  169. expect(spy).toHaveBeenCalled()
  170. done()
  171. })
  172. })
  173. })
  174. })