observer_spec.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. var Observer = require('../../../../src/observer')
  2. var config = require('../../../../src/config')
  3. var Dep = require('../../../../src/observer/dep')
  4. var _ = require('../../../../src/util')
  5. describe('Observer', function () {
  6. var spy
  7. beforeEach(function () {
  8. spy = jasmine.createSpy('observer')
  9. })
  10. it('create on non-observables', function () {
  11. // skip primitive value
  12. var ob = Observer.create(1)
  13. expect(ob).toBeUndefined()
  14. // avoid vue instance
  15. ob = Observer.create(new _.Vue())
  16. expect(ob).toBeUndefined()
  17. })
  18. it('create on object', function () {
  19. // on object
  20. var obj = {
  21. a: {},
  22. b: {}
  23. }
  24. var ob = Observer.create(obj)
  25. expect(ob instanceof Observer).toBe(true)
  26. expect(ob.active).toBe(true)
  27. expect(ob.value).toBe(obj)
  28. expect(obj.__ob__).toBe(ob)
  29. // should've walked children
  30. expect(obj.a.__ob__ instanceof Observer).toBe(true)
  31. expect(obj.b.__ob__ instanceof Observer).toBe(true)
  32. // should return existing ob on already observed objects
  33. var ob2 = Observer.create(obj)
  34. expect(ob2).toBe(ob)
  35. })
  36. it('create on array', function () {
  37. // on object
  38. var arr = [{}, {}]
  39. var ob = Observer.create(arr)
  40. expect(ob instanceof Observer).toBe(true)
  41. expect(ob.active).toBe(true)
  42. expect(ob.value).toBe(arr)
  43. expect(arr.__ob__).toBe(ob)
  44. // should've walked children
  45. expect(arr[0].__ob__ instanceof Observer).toBe(true)
  46. expect(arr[1].__ob__ instanceof Observer).toBe(true)
  47. })
  48. it('observing object prop change', function () {
  49. var obj = { a: { b: 2 } }
  50. Observer.create(obj)
  51. // mock a watcher!
  52. var watcher = {
  53. deps: [],
  54. addDep: function (dep) {
  55. this.deps.push(dep)
  56. dep.addSub(this)
  57. },
  58. update: jasmine.createSpy()
  59. }
  60. var dump
  61. // collect dep
  62. Observer.target = watcher
  63. dump = obj.a.b
  64. Observer.target = null
  65. expect(watcher.deps.length).toBe(2)
  66. dump = obj.a.b = 3
  67. expect(watcher.update.calls.count()).toBe(1)
  68. // swap object
  69. var oldA = obj.a
  70. obj.a = { b: 4 }
  71. expect(watcher.update.calls.count()).toBe(2)
  72. expect(oldA.__ob__.deps.length).toBe(0)
  73. expect(obj.a.__ob__.deps.length).toBe(1)
  74. // recollect dep
  75. var oldDeps = watcher.deps
  76. watcher.deps = []
  77. Observer.target = watcher
  78. dump = obj.a.b
  79. Observer.target = null
  80. expect(watcher.deps.length).toBe(2)
  81. // set on the swapped object
  82. obj.a.b = 5
  83. expect(watcher.update.calls.count()).toBe(3)
  84. })
  85. it('observing $add/$set/$delete', function () {
  86. var obj = { a: 1 }
  87. var ob = Observer.create(obj)
  88. var dep = new Dep()
  89. ob.deps.push(dep)
  90. spyOn(dep, 'notify')
  91. obj.$add('b', 2)
  92. expect(obj.b).toBe(2)
  93. expect(dep.notify.calls.count()).toBe(1)
  94. obj.$delete('a')
  95. expect(obj.hasOwnProperty('a')).toBe(false)
  96. expect(dep.notify.calls.count()).toBe(2)
  97. // should ignore adding an existing key
  98. obj.$add('b', 3)
  99. expect(obj.b).toBe(2)
  100. expect(dep.notify.calls.count()).toBe(2)
  101. // set existing key, should be a plain set and not
  102. // trigger own ob's notify
  103. obj.$set('b', 3)
  104. expect(obj.b).toBe(3)
  105. expect(dep.notify.calls.count()).toBe(2)
  106. // set non-existing key
  107. obj.$set('c', 1)
  108. expect(obj.c).toBe(1)
  109. expect(dep.notify.calls.count()).toBe(3)
  110. // should ignore deleting non-existing key
  111. obj.$delete('a')
  112. expect(dep.notify.calls.count()).toBe(3)
  113. // should work on non-observed objects
  114. var obj2 = { a: 1 }
  115. obj2.$delete('a')
  116. expect(obj2.hasOwnProperty('a')).toBe(false)
  117. })
  118. it('observing array mutation', function () {
  119. var arr = []
  120. var ob = Observer.create(arr)
  121. var dep = new Dep()
  122. ob.deps.push(dep)
  123. spyOn(dep, 'notify')
  124. var objs = [{}, {}, {}]
  125. arr.push(objs[0])
  126. arr.pop()
  127. arr.unshift(objs[1])
  128. arr.shift()
  129. arr.splice(0, 0, objs[2])
  130. arr.sort()
  131. arr.reverse()
  132. expect(dep.notify.calls.count()).toBe(7)
  133. // inserted elements should be observed
  134. objs.forEach(function (obj) {
  135. expect(obj.__ob__ instanceof Observer).toBe(true)
  136. })
  137. })
  138. it('array $set', function () {
  139. var arr = [1]
  140. var ob = Observer.create(arr)
  141. var dep = new Dep()
  142. ob.deps.push(dep)
  143. spyOn(dep, 'notify')
  144. arr.$set(0, 2)
  145. expect(arr[0]).toBe(2)
  146. expect(dep.notify.calls.count()).toBe(1)
  147. // setting out of bound index
  148. arr.$set(2, 3)
  149. expect(arr[2]).toBe(3)
  150. expect(dep.notify.calls.count()).toBe(2)
  151. })
  152. it('array $remove', function () {
  153. var arr = [{}, {}]
  154. var obj1 = arr[0]
  155. var obj2 = arr[1]
  156. var ob = Observer.create(arr)
  157. var dep = new Dep()
  158. ob.deps.push(dep)
  159. spyOn(dep, 'notify')
  160. // remove by index
  161. arr.$remove(0)
  162. expect(arr.length).toBe(1)
  163. expect(arr[0]).toBe(obj2)
  164. expect(dep.notify.calls.count()).toBe(1)
  165. // remove by identity, not in array
  166. arr.$remove(obj1)
  167. expect(arr.length).toBe(1)
  168. expect(arr[0]).toBe(obj2)
  169. expect(dep.notify.calls.count()).toBe(1)
  170. // remove by identity, in array
  171. arr.$remove(obj2)
  172. expect(arr.length).toBe(0)
  173. expect(dep.notify.calls.count()).toBe(2)
  174. })
  175. it('no proto', function () {
  176. config.proto = false
  177. // object
  178. var obj = {a:1}
  179. var ob = Observer.create(obj)
  180. expect(obj.$add).toBeTruthy()
  181. expect(obj.$delete).toBeTruthy()
  182. var dep = new Dep()
  183. ob.deps.push(dep)
  184. spyOn(dep, 'notify')
  185. obj.$add('b', 2)
  186. expect(dep.notify).toHaveBeenCalled()
  187. // array
  188. var arr = [1, 2, 3]
  189. var ob2 = Observer.create(arr)
  190. expect(arr.$set).toBeTruthy()
  191. expect(arr.$remove).toBeTruthy()
  192. expect(arr.push).not.toBe([].push)
  193. var dep2 = new Dep()
  194. ob2.deps.push(dep2)
  195. spyOn(dep2, 'notify')
  196. arr.push(1)
  197. expect(dep2.notify).toHaveBeenCalled()
  198. config.proto = true
  199. })
  200. })