observer_spec.js 5.5 KB

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