observer_spec.js 5.2 KB

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