watcher.spec.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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 = jasmine.createSpy('watcher')
  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. }).then(() => {
  29. expect(watcher.value).toBe(4)
  30. expect(spy).toHaveBeenCalledWith(4, 3)
  31. }).then(done)
  32. })
  33. it('non-existent path, set later', done => {
  34. const watcher1 = new Watcher(vm, 'b.e', spy)
  35. expect(watcher1.value).toBeUndefined()
  36. // check $add should not affect isolated children
  37. const child2 = new Vue({ parent: vm })
  38. const watcher2 = new Watcher(child2, 'b.e', spy)
  39. expect(watcher2.value).toBeUndefined()
  40. Vue.set(vm.b, 'e', 123)
  41. waitForUpdate(() => {
  42. expect(watcher1.value).toBe(123)
  43. expect(watcher2.value).toBeUndefined()
  44. expect(spy.calls.count()).toBe(1)
  45. expect(spy).toHaveBeenCalledWith(123, undefined)
  46. }).then(done)
  47. })
  48. it('delete', done => {
  49. const watcher = new Watcher(vm, 'b.c', spy)
  50. expect(watcher.value).toBe(2)
  51. Vue.delete(vm.b, 'c')
  52. waitForUpdate(() => {
  53. expect(watcher.value).toBeUndefined()
  54. expect(spy).toHaveBeenCalledWith(undefined, 2)
  55. }).then(done)
  56. })
  57. it('path containing $data', done => {
  58. const watcher = new Watcher(vm, '$data.b.c', spy)
  59. expect(watcher.value).toBeUndefined()
  60. vm.b = { c: 3 }
  61. waitForUpdate(() => {
  62. expect(watcher.value).toBeUndefined()
  63. expect(spy).not.toHaveBeenCalledWith(3, 2)
  64. vm.$data = { b: { c: 4 }}
  65. }).then(() => {
  66. expect(watcher.value).toBeUndefined()
  67. expect(spy).not.toHaveBeenCalledWith(4, 3)
  68. }).then(done)
  69. })
  70. it('not watching $data', done => {
  71. const oldData = vm.$data
  72. const watcher = new Watcher(vm, '$data', spy)
  73. expect(watcher.value).toBeUndefined()
  74. const newData = {}
  75. vm.$data = newData
  76. waitForUpdate(() => {
  77. expect(spy).not.toHaveBeenCalledWith(newData, oldData)
  78. expect(watcher.value).toBeUndefined()
  79. }).then(done)
  80. })
  81. it('deep watch', done => {
  82. let oldB
  83. new Watcher(vm, 'b', spy, {
  84. deep: true
  85. })
  86. vm.b.c = { d: 4 }
  87. waitForUpdate(() => {
  88. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  89. oldB = vm.b
  90. vm.b = { c: [{ a: 1 }] }
  91. }).then(() => {
  92. expect(spy).toHaveBeenCalledWith(vm.b, oldB)
  93. expect(spy.calls.count()).toBe(2)
  94. vm.b.c[0].a = 2
  95. }).then(() => {
  96. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  97. expect(spy.calls.count()).toBe(3)
  98. }).then(done)
  99. })
  100. it('deep watch with circular references', done => {
  101. new Watcher(vm, 'b', spy, {
  102. deep: true
  103. })
  104. Vue.set(vm.b, '_', vm.b)
  105. waitForUpdate(() => {
  106. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  107. expect(spy.calls.count()).toBe(1)
  108. vm.b._.c = 1
  109. }).then(() => {
  110. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  111. expect(spy.calls.count()).toBe(2)
  112. }).then(done)
  113. })
  114. it('fire change for prop addition/deletion in non-deep mode', done => {
  115. new Watcher(vm, 'b', spy)
  116. Vue.set(vm.b, 'e', 123)
  117. waitForUpdate(() => {
  118. expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
  119. expect(spy.calls.count()).toBe(1)
  120. Vue.delete(vm.b, 'e')
  121. }).then(() => {
  122. expect(spy.calls.count()).toBe(2)
  123. }).then(done)
  124. })
  125. it('watch function', done => {
  126. const watcher = new Watcher(vm, function () {
  127. return this.a + this.b.d
  128. }, spy)
  129. expect(watcher.value).toBe(5)
  130. vm.a = 2
  131. waitForUpdate(() => {
  132. expect(spy).toHaveBeenCalledWith(6, 5)
  133. vm.b = { d: 2 }
  134. }).then(() => {
  135. expect(spy).toHaveBeenCalledWith(4, 6)
  136. }).then(done)
  137. })
  138. it('lazy mode', done => {
  139. const watcher = new Watcher(vm, function () {
  140. return this.a + this.b.d
  141. }, null, { lazy: true })
  142. expect(watcher.lazy).toBe(true)
  143. expect(watcher.value).toBeUndefined()
  144. expect(watcher.dirty).toBe(true)
  145. watcher.evaluate()
  146. expect(watcher.value).toBe(5)
  147. expect(watcher.dirty).toBe(false)
  148. vm.a = 2
  149. waitForUpdate(() => {
  150. expect(watcher.value).toBe(5)
  151. expect(watcher.dirty).toBe(true)
  152. watcher.evaluate()
  153. expect(watcher.value).toBe(6)
  154. expect(watcher.dirty).toBe(false)
  155. }).then(done)
  156. })
  157. it('teardown', done => {
  158. const watcher = new Watcher(vm, 'b.c', spy)
  159. watcher.teardown()
  160. vm.b.c = 3
  161. waitForUpdate(() => {
  162. expect(watcher.active).toBe(false)
  163. expect(spy).not.toHaveBeenCalled()
  164. }).then(done)
  165. })
  166. it('warn not support path', () => {
  167. new Watcher(vm, 'd.e + c', spy)
  168. expect('Failed watching path:').toHaveBeenWarned()
  169. })
  170. it('catch getter error', () => {
  171. Vue.config.errorHandler = spy
  172. const err = new Error()
  173. const vm = new Vue({
  174. render () { throw err }
  175. }).$mount()
  176. expect('Error during component render').toHaveBeenWarned()
  177. expect(spy).toHaveBeenCalledWith(err, vm)
  178. Vue.config.errorHandler = null
  179. })
  180. it('catch user watcher error', () => {
  181. Vue.config.errorHandler = spy
  182. new Watcher(vm, function () {
  183. return this.a.b.c
  184. }, () => {}, { user: true })
  185. expect('Error when evaluating watcher').toHaveBeenWarned()
  186. expect(spy).toHaveBeenCalled()
  187. expect(spy.calls.argsFor(0)[0] instanceof TypeError).toBe(true)
  188. expect(spy.calls.argsFor(0)[1]).toBe(vm)
  189. Vue.config.errorHandler = null
  190. })
  191. })