observer_spec.js 5.3 KB

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