observer_spec.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. var Observer = require('../../../../src/observer')
  2. var Dep = require('../../../../src/observer/dep')
  3. var _ = require('../../../../src/util')
  4. describe('Observer', function () {
  5. beforeEach(function () {
  6. spyOn(_, 'warn')
  7. })
  8. it('create on non-observables', function () {
  9. // skip primitive value
  10. var ob = Observer.create(1)
  11. expect(ob).toBeUndefined()
  12. // avoid vue instance
  13. ob = Observer.create(new _.Vue())
  14. expect(ob).toBeUndefined()
  15. // avoid frozen objects
  16. ob = Observer.create(Object.freeze({}))
  17. expect(ob).toBeUndefined()
  18. })
  19. it('create on object', function () {
  20. // on object
  21. var obj = {
  22. a: {},
  23. b: {}
  24. }
  25. var ob = Observer.create(obj)
  26. expect(ob instanceof Observer).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.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 (dep) {
  54. this.deps.push(dep)
  55. dep.addSub(this)
  56. },
  57. update: jasmine.createSpy()
  58. }
  59. // collect dep
  60. Dep.target = watcher
  61. obj.a.b
  62. Dep.target = null
  63. expect(watcher.deps.length).toBe(3) // obj.a + a.b + b
  64. obj.a.b = 3
  65. expect(watcher.update.calls.count()).toBe(1)
  66. // swap object
  67. obj.a = { b: 4 }
  68. expect(watcher.update.calls.count()).toBe(2)
  69. watcher.deps = []
  70. Dep.target = watcher
  71. obj.a.b
  72. Dep.target = null
  73. expect(watcher.deps.length).toBe(3)
  74. // set on the swapped object
  75. obj.a.b = 5
  76. expect(watcher.update.calls.count()).toBe(3)
  77. })
  78. it('observing set/delete', function () {
  79. var obj = { a: 1 }
  80. var ob = Observer.create(obj)
  81. var dep = ob.dep
  82. spyOn(dep, 'notify')
  83. _.set(obj, 'b', 2)
  84. expect(obj.b).toBe(2)
  85. expect(dep.notify.calls.count()).toBe(1)
  86. _.delete(obj, 'a')
  87. expect(obj.hasOwnProperty('a')).toBe(false)
  88. expect(dep.notify.calls.count()).toBe(2)
  89. // set existing key, should be a plain set and not
  90. // trigger own ob's notify
  91. _.set(obj, 'b', 3)
  92. expect(obj.b).toBe(3)
  93. expect(dep.notify.calls.count()).toBe(2)
  94. // set non-existing key
  95. _.set(obj, 'c', 1)
  96. expect(obj.c).toBe(1)
  97. expect(dep.notify.calls.count()).toBe(3)
  98. // should ignore deleting non-existing key
  99. _.delete(obj, 'a')
  100. expect(dep.notify.calls.count()).toBe(3)
  101. // should work on non-observed objects
  102. var obj2 = { a: 1 }
  103. _.delete(obj2, 'a')
  104. expect(obj2.hasOwnProperty('a')).toBe(false)
  105. })
  106. it('observing array mutation', function () {
  107. var arr = []
  108. var ob = Observer.create(arr)
  109. var dep = ob.dep
  110. spyOn(dep, 'notify')
  111. var objs = [{}, {}, {}]
  112. arr.push(objs[0])
  113. arr.pop()
  114. arr.unshift(objs[1])
  115. arr.shift()
  116. arr.splice(0, 0, objs[2])
  117. arr.sort()
  118. arr.reverse()
  119. expect(dep.notify.calls.count()).toBe(7)
  120. // inserted elements should be observed
  121. objs.forEach(function (obj) {
  122. expect(obj.__ob__ instanceof Observer).toBe(true)
  123. })
  124. })
  125. it('array $set', function () {
  126. var arr = [1]
  127. var ob = Observer.create(arr)
  128. var dep = ob.dep
  129. spyOn(dep, 'notify')
  130. arr.$set(0, 2)
  131. expect(arr[0]).toBe(2)
  132. expect(dep.notify.calls.count()).toBe(1)
  133. // setting out of bound index
  134. arr.$set(2, 3)
  135. expect(arr[2]).toBe(3)
  136. expect(dep.notify.calls.count()).toBe(2)
  137. })
  138. it('array $remove', function () {
  139. var arr = [{}, {}]
  140. var obj1 = arr[0]
  141. var obj2 = arr[1]
  142. var ob = Observer.create(arr)
  143. var dep = ob.dep
  144. spyOn(dep, 'notify')
  145. // remove by identity, not in array
  146. arr.$remove(obj1)
  147. expect(arr.length).toBe(1)
  148. expect(arr[0]).toBe(obj2)
  149. expect(dep.notify.calls.count()).toBe(1)
  150. // remove by identity, in array
  151. arr.$remove(obj2)
  152. expect(arr.length).toBe(0)
  153. expect(dep.notify.calls.count()).toBe(2)
  154. })
  155. it('no proto', function () {
  156. _.hasProto = false
  157. var arr = [1, 2, 3]
  158. var ob2 = Observer.create(arr)
  159. expect(arr.$set).toBeTruthy()
  160. expect(arr.$remove).toBeTruthy()
  161. expect(arr.push).not.toBe([].push)
  162. var dep2 = ob2.dep
  163. spyOn(dep2, 'notify')
  164. arr.push(1)
  165. expect(dep2.notify).toHaveBeenCalled()
  166. _.hasProto = true
  167. })
  168. })