github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/vue-1.0.24/test/unit/specs/observer/observer_spec.js (about) 1 var Vue = require('src') 2 var ob = require('src/observer') 3 var Observer = ob.Observer 4 var observe = ob.observe 5 var Dep = require('src/observer/dep') 6 var _ = require('src/util') 7 8 describe('Observer', function () { 9 it('create on non-observables', function () { 10 // skip primitive value 11 var ob = observe(1) 12 expect(ob).toBeUndefined() 13 // avoid vue instance 14 ob = observe(new Vue()) 15 expect(ob).toBeUndefined() 16 // avoid frozen objects 17 ob = observe(Object.freeze({})) 18 expect(ob).toBeUndefined() 19 }) 20 21 it('create on object', function () { 22 // on object 23 var obj = { 24 a: {}, 25 b: {} 26 } 27 var ob = observe(obj) 28 expect(ob instanceof Observer).toBe(true) 29 expect(ob.value).toBe(obj) 30 expect(obj.__ob__).toBe(ob) 31 // should've walked children 32 expect(obj.a.__ob__ instanceof Observer).toBe(true) 33 expect(obj.b.__ob__ instanceof Observer).toBe(true) 34 // should return existing ob on already observed objects 35 var ob2 = observe(obj) 36 expect(ob2).toBe(ob) 37 }) 38 39 it('create on null', function () { 40 // on null 41 var obj = Object.create(null) 42 obj.a = {} 43 obj.b = {} 44 var ob = observe(obj) 45 expect(ob instanceof Observer).toBe(true) 46 expect(ob.value).toBe(obj) 47 expect(obj.__ob__).toBe(ob) 48 // should've walked children 49 expect(obj.a.__ob__ instanceof Observer).toBe(true) 50 expect(obj.b.__ob__ instanceof Observer).toBe(true) 51 // should return existing ob on already observed objects 52 var ob2 = observe(obj) 53 expect(ob2).toBe(ob) 54 }) 55 56 it('create on already observed object', function () { 57 // on object 58 var obj = {} 59 var val = 0 60 var getCount = 0 61 Object.defineProperty(obj, 'a', { 62 configurable: true, 63 enumerable: true, 64 get: function () { 65 getCount++ 66 return val 67 }, 68 set: function (v) { 69 val = v 70 } 71 }) 72 73 var ob = observe(obj) 74 expect(ob instanceof Observer).toBe(true) 75 expect(ob.value).toBe(obj) 76 expect(obj.__ob__).toBe(ob) 77 78 getCount = 0 79 // Each read of 'a' should result in only one get underlying get call 80 obj.a 81 expect(getCount).toBe(1) 82 obj.a 83 expect(getCount).toBe(2) 84 85 // should return existing ob on already observed objects 86 var ob2 = observe(obj) 87 expect(ob2).toBe(ob) 88 89 // should call underlying setter 90 obj.a = 10 91 expect(val).toBe(10) 92 }) 93 94 it('create on property with only getter', function () { 95 // on object 96 var obj = {} 97 Object.defineProperty(obj, 'a', { 98 configurable: true, 99 enumerable: true, 100 get: function () { 101 return 123 102 } 103 }) 104 105 var ob = observe(obj) 106 expect(ob instanceof Observer).toBe(true) 107 expect(ob.value).toBe(obj) 108 expect(obj.__ob__).toBe(ob) 109 110 // should be able to read 111 expect(obj.a).toBe(123) 112 113 // should return existing ob on already observed objects 114 var ob2 = observe(obj) 115 expect(ob2).toBe(ob) 116 117 // since there is no setter, you shouldn't be able to write to it 118 // PhantomJS throws when a property with no setter is set 119 // but other real browsers don't 120 try { 121 obj.a = 101 122 } catch (e) {} 123 expect(obj.a).toBe(123) 124 }) 125 126 it('create on property with only setter', function () { 127 // on object 128 var obj = {} 129 var val = 10 130 Object.defineProperty(obj, 'a', { // eslint-disable-line accessor-pairs 131 configurable: true, 132 enumerable: true, 133 set: function (v) { 134 val = v 135 } 136 }) 137 138 var ob = observe(obj) 139 expect(ob instanceof Observer).toBe(true) 140 expect(ob.value).toBe(obj) 141 expect(obj.__ob__).toBe(ob) 142 143 // reads should return undefined 144 expect(obj.a).toBe(undefined) 145 146 // should return existing ob on already observed objects 147 var ob2 = observe(obj) 148 expect(ob2).toBe(ob) 149 150 // writes should call the set function 151 obj.a = 100 152 expect(val).toBe(100) 153 }) 154 155 it('create on property which is marked not configurable', function () { 156 // on object 157 var obj = {} 158 Object.defineProperty(obj, 'a', { 159 configurable: false, 160 enumerable: true, 161 val: 10 162 }) 163 164 var ob = observe(obj) 165 expect(ob instanceof Observer).toBe(true) 166 expect(ob.value).toBe(obj) 167 expect(obj.__ob__).toBe(ob) 168 }) 169 170 it('create on array', function () { 171 // on object 172 var arr = [{}, {}] 173 var ob = observe(arr) 174 expect(ob instanceof Observer).toBe(true) 175 expect(ob.value).toBe(arr) 176 expect(arr.__ob__).toBe(ob) 177 // should've walked children 178 expect(arr[0].__ob__ instanceof Observer).toBe(true) 179 expect(arr[1].__ob__ instanceof Observer).toBe(true) 180 }) 181 182 it('observing object prop change', function () { 183 var obj = { a: { b: 2 } } 184 observe(obj) 185 // mock a watcher! 186 var watcher = { 187 deps: [], 188 addDep: function (dep) { 189 this.deps.push(dep) 190 dep.addSub(this) 191 }, 192 update: jasmine.createSpy() 193 } 194 // collect dep 195 Dep.target = watcher 196 obj.a.b 197 Dep.target = null 198 expect(watcher.deps.length).toBe(3) // obj.a + a.b + b 199 obj.a.b = 3 200 expect(watcher.update.calls.count()).toBe(1) 201 // swap object 202 obj.a = { b: 4 } 203 expect(watcher.update.calls.count()).toBe(2) 204 watcher.deps = [] 205 Dep.target = watcher 206 obj.a.b 207 Dep.target = null 208 expect(watcher.deps.length).toBe(3) 209 // set on the swapped object 210 obj.a.b = 5 211 expect(watcher.update.calls.count()).toBe(3) 212 }) 213 214 it('observing object prop change on defined property', function () { 215 var obj = { val: 2 } 216 Object.defineProperty(obj, 'a', { 217 configurable: true, 218 enumerable: true, 219 get: function () { 220 return this.val 221 }, 222 set: function (v) { 223 this.val = v 224 return this.val 225 } 226 }) 227 228 observe(obj) 229 // mock a watcher! 230 var watcher = { 231 deps: [], 232 addDep: function (dep) { 233 this.deps.push(dep) 234 dep.addSub(this) 235 }, 236 update: jasmine.createSpy() 237 } 238 // collect dep 239 Dep.target = watcher 240 expect(obj.a).toBe(2) // Make sure 'this' is preserved 241 Dep.target = null 242 obj.a = 3 243 expect(obj.val).toBe(3) // make sure 'setter' was called 244 obj.val = 5 245 expect(obj.a).toBe(5) // make sure 'getter' was called 246 }) 247 248 it('observing set/delete', function () { 249 var obj = { a: 1 } 250 var ob = observe(obj) 251 var dep = ob.dep 252 spyOn(dep, 'notify') 253 _.set(obj, 'b', 2) 254 expect(obj.b).toBe(2) 255 expect(dep.notify.calls.count()).toBe(1) 256 _.del(obj, 'a') 257 expect(_.hasOwn(obj, 'a')).toBe(false) 258 expect(dep.notify.calls.count()).toBe(2) 259 // set existing key, should be a plain set and not 260 // trigger own ob's notify 261 _.set(obj, 'b', 3) 262 expect(obj.b).toBe(3) 263 expect(dep.notify.calls.count()).toBe(2) 264 // set non-existing key 265 _.set(obj, 'c', 1) 266 expect(obj.c).toBe(1) 267 expect(dep.notify.calls.count()).toBe(3) 268 // should ignore deleting non-existing key 269 _.del(obj, 'a') 270 expect(dep.notify.calls.count()).toBe(3) 271 // should work on non-observed objects 272 var obj2 = { a: 1 } 273 _.del(obj2, 'a') 274 expect(_.hasOwn(obj2, 'a')).toBe(false) 275 // should work on Object.create(null) 276 var obj3 = Object.create(null) 277 obj3.a = 1 278 var ob3 = observe(obj3) 279 var dep3 = ob3.dep 280 spyOn(dep3, 'notify') 281 _.set(obj3, 'b', 2) 282 expect(obj3.b).toBe(2) 283 expect(dep3.notify.calls.count()).toBe(1) 284 _.del(obj3, 'a') 285 expect(_.hasOwn(obj3, 'a')).toBe(false) 286 expect(dep3.notify.calls.count()).toBe(2) 287 }) 288 289 it('observing set/delete in Vm object', function (done) { 290 var el = document.createElement('div') 291 var vm = new Vue({ 292 el: el, 293 template: '<div>{{a}}</div>', 294 data: { a: 1 } 295 }) 296 expect(el.innerHTML).toBe('<div>1</div>') 297 Vue.set(vm, 'a', 2) 298 Vue.nextTick(function () { 299 expect(el.innerHTML).toBe('<div>2</div>') 300 Vue.delete(vm, 'a') 301 Vue.nextTick(function () { 302 expect(el.innerHTML).toBe('<div></div>') 303 done() 304 }) 305 }) 306 }) 307 308 it('observing array mutation', function () { 309 var arr = [] 310 var ob = observe(arr) 311 var dep = ob.dep 312 spyOn(dep, 'notify') 313 var objs = [{}, {}, {}] 314 arr.push(objs[0]) 315 arr.pop() 316 arr.unshift(objs[1]) 317 arr.shift() 318 arr.splice(0, 0, objs[2]) 319 arr.sort() 320 arr.reverse() 321 expect(dep.notify.calls.count()).toBe(7) 322 // inserted elements should be observed 323 objs.forEach(function (obj) { 324 expect(obj.__ob__ instanceof Observer).toBe(true) 325 }) 326 }) 327 328 it('array $set', function () { 329 var arr = [1] 330 var ob = observe(arr) 331 var dep = ob.dep 332 spyOn(dep, 'notify') 333 arr.$set(0, 2) 334 expect(arr[0]).toBe(2) 335 expect(dep.notify.calls.count()).toBe(1) 336 // setting out of bound index 337 arr.$set(2, 3) 338 expect(arr[2]).toBe(3) 339 expect(dep.notify.calls.count()).toBe(2) 340 }) 341 342 it('array $remove', function () { 343 var arr = [{}, {}] 344 var obj1 = arr[0] 345 var obj2 = arr[1] 346 var ob = observe(arr) 347 var dep = ob.dep 348 spyOn(dep, 'notify') 349 // remove by identity, not in array 350 arr.$remove(obj1) 351 expect(arr.length).toBe(1) 352 expect(arr[0]).toBe(obj2) 353 expect(dep.notify.calls.count()).toBe(1) 354 // remove by identity, in array 355 arr.$remove(obj2) 356 expect(arr.length).toBe(0) 357 expect(dep.notify.calls.count()).toBe(2) 358 }) 359 360 it('no proto', function () { 361 _.hasProto = false 362 var arr = [1, 2, 3] 363 var ob2 = observe(arr) 364 expect(arr.$set).toBeTruthy() 365 expect(arr.$remove).toBeTruthy() 366 expect(arr.push).not.toBe([].push) 367 var dep2 = ob2.dep 368 spyOn(dep2, 'notify') 369 arr.push(1) 370 expect(dep2.notify).toHaveBeenCalled() 371 _.hasProto = true 372 }) 373 })