watcher.spec.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import Vue from 'vue'
  2. import Watcher from 'core/observer/watcher'
  3. describe('Watcher', () => {
  4. let vm, spy
  5. beforeEach(() => {
  6. vm = new Vue({
  7. template: '<div></div>',
  8. data: {
  9. a: 1,
  10. b: {
  11. c: 2,
  12. d: 4
  13. },
  14. c: 'c',
  15. msg: 'yo'
  16. }
  17. }).$mount()
  18. spy = vi.fn()
  19. })
  20. it('path', done => {
  21. const watcher = new Watcher(vm, 'b.c', spy)
  22. expect(watcher.value).toBe(2)
  23. vm.b.c = 3
  24. waitForUpdate(() => {
  25. expect(watcher.value).toBe(3)
  26. expect(spy).toHaveBeenCalledWith(3, 2)
  27. vm.b = { c: 4 } // swapping the object
  28. })
  29. .then(() => {
  30. expect(watcher.value).toBe(4)
  31. expect(spy).toHaveBeenCalledWith(4, 3)
  32. })
  33. .then(done)
  34. })
  35. it('non-existent path, set later', done => {
  36. const watcher1 = new Watcher(vm, 'b.e', spy)
  37. expect(watcher1.value).toBeUndefined()
  38. // check $add should not affect isolated children
  39. const child2 = new Vue({ parent: vm })
  40. const watcher2 = new Watcher(child2, 'b.e', spy)
  41. expect(watcher2.value).toBeUndefined()
  42. Vue.set(vm.b, 'e', 123)
  43. waitForUpdate(() => {
  44. expect(watcher1.value).toBe(123)
  45. expect(watcher2.value).toBeUndefined()
  46. expect(spy.mock.calls.length).toBe(1)
  47. expect(spy).toHaveBeenCalledWith(123, undefined)
  48. }).then(done)
  49. })
  50. it('delete', done => {
  51. const watcher = new Watcher(vm, 'b.c', spy)
  52. expect(watcher.value).toBe(2)
  53. Vue.delete(vm.b, 'c')
  54. waitForUpdate(() => {
  55. expect(watcher.value).toBeUndefined()
  56. expect(spy).toHaveBeenCalledWith(undefined, 2)
  57. }).then(done)
  58. })
  59. it('path containing $data', done => {
  60. const watcher = new Watcher(vm, '$data.b.c', spy)
  61. expect(watcher.value).toBe(2)
  62. vm.b = { c: 3 }
  63. waitForUpdate(() => {
  64. expect(watcher.value).toBe(3)
  65. expect(spy).toHaveBeenCalledWith(3, 2)
  66. vm.$data.b.c = 4
  67. })
  68. .then(() => {
  69. expect(watcher.value).toBe(4)
  70. expect(spy).toHaveBeenCalledWith(4, 3)
  71. })
  72. .then(done)
  73. })
  74. it('deep watch', done => {
  75. let oldB
  76. new Watcher(vm, 'b', spy, {
  77. deep: true
  78. })
  79. vm.b.c = { d: 4 }
  80. waitForUpdate(() => {
  81. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  82. oldB = vm.b
  83. vm.b = { c: [{ a: 1 }] }
  84. })
  85. .then(() => {
  86. expect(spy).toHaveBeenCalledWith(vm.b, oldB)
  87. expect(spy.mock.calls.length).toBe(2)
  88. vm.b.c[0].a = 2
  89. })
  90. .then(() => {
  91. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  92. expect(spy.mock.calls.length).toBe(3)
  93. })
  94. .then(done)
  95. })
  96. it('deep watch $data', done => {
  97. new Watcher(vm, '$data', spy, {
  98. deep: true
  99. })
  100. vm.b.c = 3
  101. waitForUpdate(() => {
  102. expect(spy).toHaveBeenCalledWith(vm.$data, vm.$data)
  103. }).then(done)
  104. })
  105. it('deep watch with circular references', done => {
  106. new Watcher(vm, 'b', spy, {
  107. deep: true
  108. })
  109. Vue.set(vm.b, '_', vm.b)
  110. waitForUpdate(() => {
  111. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  112. expect(spy.mock.calls.length).toBe(1)
  113. vm.b._.c = 1
  114. })
  115. .then(() => {
  116. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  117. expect(spy.mock.calls.length).toBe(2)
  118. })
  119. .then(done)
  120. })
  121. it('fire change for prop addition/deletion in non-deep mode', done => {
  122. new Watcher(vm, 'b', spy)
  123. Vue.set(vm.b, 'e', 123)
  124. waitForUpdate(() => {
  125. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  126. expect(spy.mock.calls.length).toBe(1)
  127. Vue.delete(vm.b, 'e')
  128. })
  129. .then(() => {
  130. expect(spy.mock.calls.length).toBe(2)
  131. })
  132. .then(done)
  133. })
  134. it('watch function', done => {
  135. const watcher = new Watcher(
  136. vm,
  137. function () {
  138. return this.a + this.b.d
  139. },
  140. spy
  141. )
  142. expect(watcher.value).toBe(5)
  143. vm.a = 2
  144. waitForUpdate(() => {
  145. expect(spy).toHaveBeenCalledWith(6, 5)
  146. vm.b = { d: 2 }
  147. })
  148. .then(() => {
  149. expect(spy).toHaveBeenCalledWith(4, 6)
  150. })
  151. .then(done)
  152. })
  153. it('lazy mode', done => {
  154. const watcher = new Watcher(
  155. vm,
  156. function () {
  157. return this.a + this.b.d
  158. },
  159. null,
  160. { lazy: true }
  161. )
  162. expect(watcher.lazy).toBe(true)
  163. expect(watcher.value).toBeUndefined()
  164. expect(watcher.dirty).toBe(true)
  165. watcher.evaluate()
  166. expect(watcher.value).toBe(5)
  167. expect(watcher.dirty).toBe(false)
  168. vm.a = 2
  169. waitForUpdate(() => {
  170. expect(watcher.value).toBe(5)
  171. expect(watcher.dirty).toBe(true)
  172. watcher.evaluate()
  173. expect(watcher.value).toBe(6)
  174. expect(watcher.dirty).toBe(false)
  175. }).then(done)
  176. })
  177. it('teardown', done => {
  178. const watcher = new Watcher(vm, 'b.c', spy)
  179. watcher.teardown()
  180. vm.b.c = 3
  181. waitForUpdate(() => {
  182. expect(watcher.active).toBe(false)
  183. expect(spy).not.toHaveBeenCalled()
  184. }).then(done)
  185. })
  186. it('warn not support path', () => {
  187. new Watcher(vm, 'd.e + c', spy)
  188. expect('Failed watching path:').toHaveBeenWarned()
  189. })
  190. })