Przeglądaj źródła

tests for observe

Evan You 11 lat temu
rodzic
commit
0177d5dd8b
4 zmienionych plików z 166 dodań i 18 usunięć
  1. 3 0
      src/instance/init.js
  2. 0 1
      src/instance/scope.js
  3. 10 15
      src/observer/index.js
  4. 153 2
      test/unit/specs/observer_spec.js

+ 3 - 0
src/instance/init.js

@@ -23,6 +23,9 @@ exports._init = function (options) {
   this._userWatchers = {}
   this._directives   = []
 
+  // a flag to avoid this being observed
+  this._isVue = true
+
   // events bookkeeping
   this._events         = {}
   this._eventsCount    = {}

+ 0 - 1
src/instance/scope.js

@@ -16,7 +16,6 @@ var Binding = require('../binding')
  */
 
 exports._initScope = function () {
-  // this._initObserver()
   this._initData()
   this._initComputed()
   this._initMethods()

+ 10 - 15
src/observer/index.js

@@ -26,18 +26,15 @@ var OBJECT = 1
 function Observer (value, type) {
   this.id = ++uid
   this.value = value
-  this.type = type
   this.active = true
   this.binding = new Binding()
-  if (value) {
-    _.define(value, '__ob__', this)
-    if (type === ARRAY) {
-      _.augment(value, arrayAugmentations)
-      this.observeArray(value)
-    } else if (type === OBJECT) {
-      _.augment(value, objectAugmentations)
-      this.walk(value)
-    }
+  _.define(value, '__ob__', this)
+  if (type === ARRAY) {
+    _.augment(value, arrayAugmentations)
+    this.observeArray(value)
+  } else if (type === OBJECT) {
+    _.augment(value, objectAugmentations)
+    this.walk(value)
   }
 }
 
@@ -66,7 +63,7 @@ Observer.create = function (value) {
     return new Observer(value, ARRAY)
   } else if (
     _.isPlainObject(value) &&
-    !value.$observer // avoid Vue instance
+    !value._isVue // avoid Vue instance
   ) {
     return new Observer(value, OBJECT)
   }
@@ -99,14 +96,12 @@ p.walk = function (obj) {
  * and if value is array, link binding to the array.
  *
  * @param {*} val
+ * @return {Binding|undefined}
  */
 
 p.observe = function (val) {
   var ob = Observer.create(val)
-  if (ob) {
-    // ob.parentCount++
-    return ob.binding
-  }
+  if (ob) return ob.binding
 }
 
 /**

+ 153 - 2
test/unit/specs/observer_spec.js

@@ -8,8 +8,159 @@ describe('Observer', function () {
     spy = jasmine.createSpy('observer')
   })
 
-  it('should work', function () {
-    // TODO
+  it('create on non-observables', function () {
+    // skip primitive value
+    var ob = Observer.create(1)
+    expect(ob).toBeUndefined()
+    // avoid vue instance
+    ob = Observer.create(new _.Vue())
+    expect(ob).toBeUndefined()
+  })
+
+  it('create on object', function () {
+    // on object
+    var obj = {
+      a: {},
+      b: {}
+    }
+    ob = Observer.create(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.active).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+    // should've walked children
+    expect(obj.a.__ob__ instanceof Observer).toBe(true)
+    expect(obj.b.__ob__ instanceof Observer).toBe(true)
+    // should return existing ob on already observed objects
+    var ob2 = Observer.create(obj)
+    expect(ob2).toBe(ob)
+  })
+
+  it('create on array', function () {
+    // on object
+    var arr = [{}, {}]
+    ob = Observer.create(arr)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.active).toBe(true)
+    expect(ob.value).toBe(arr)
+    expect(arr.__ob__).toBe(ob)
+    // should've walked children
+    expect(arr[0].__ob__ instanceof Observer).toBe(true)
+    expect(arr[1].__ob__ instanceof Observer).toBe(true)
+  })
+
+  it('observing object prop change', function () {
+    var obj = { a: { b: 2 } }
+    Observer.create(obj)
+    // mock a watcher!
+    var watcher = {
+      deps: [],
+      addDep: function (binding) {
+        this.deps.push(binding)
+        binding.addSub(this)
+      },
+      update: jasmine.createSpy()
+    }
+    // collect dep
+    Observer.target = watcher
+    obj.a.b
+    Observer.target = null
+    expect(watcher.deps.length).toBe(2)
+    obj.a.b = 3
+    expect(watcher.update.calls.count()).toBe(1)
+    // swap object
+    obj.a = { b: 4 }
+    expect(watcher.update.calls.count()).toBe(2)
+    // recollect dep
+    var oldDeps = watcher.deps
+    watcher.deps = []
+    Observer.target = watcher
+    obj.a.b
+    Observer.target = null
+    expect(watcher.deps.length).toBe(2)
+    // make sure we picked up the new bindings
+    expect(watcher.deps[0]).not.toBe(oldDeps[0])
+    expect(watcher.deps[1]).not.toBe(oldDeps[1])
+    // set on the swapped object
+    obj.a.b = 5
+    expect(watcher.update.calls.count()).toBe(3)
+  })
+
+  it('observing $add/$delete', function () {
+    var obj = { a: 1 }
+    var ob = Observer.create(obj)
+    var binding = ob.binding
+    spyOn(binding, 'notify')
+    obj.$add('b', 2)
+    expect(obj.b).toBe(2)
+    expect(binding.notify.calls.count()).toBe(1)
+    obj.$delete('a')
+    expect(obj.hasOwnProperty('a')).toBe(false)
+    expect(binding.notify.calls.count()).toBe(2)
+    // should ignore adding an existing key
+    obj.$add('b', 3)
+    expect(obj.b).toBe(2)
+    expect(binding.notify.calls.count()).toBe(2)
+    // should ignore deleting non-existing key
+    obj.$delete('a')
+    expect(binding.notify.calls.count()).toBe(2)
+  })
+
+  it('observing array mutation', function () {
+    var arr = []
+    var ob = Observer.create(arr)
+    var binding = ob.binding
+    spyOn(binding, 'notify')
+    var objs = [{}, {}, {}]
+    arr.push(objs[0])
+    arr.pop()
+    arr.unshift(objs[1])
+    arr.shift()
+    arr.splice(0, 0, objs[2])
+    arr.sort()
+    arr.reverse()
+    expect(binding.notify.calls.count()).toBe(7)
+    // inserted elements should be observed
+    objs.forEach(function (obj) {
+      expect(obj.__ob__ instanceof Observer).toBe(true)
+    })
+  })
+
+  it('array $set', function () {
+    var arr = [1]
+    var ob = Observer.create(arr)
+    var binding = ob.binding
+    spyOn(binding, 'notify')
+    arr.$set(0, 2)
+    expect(arr[0]).toBe(2)
+    expect(binding.notify.calls.count()).toBe(1)
+    // setting out of bound index
+    arr.$set(2, 3)
+    expect(arr[2]).toBe(3)
+    expect(binding.notify.calls.count()).toBe(2)
+  })
+
+  it('array $remove', function () {
+    var arr = [{}, {}]
+    var obj1 = arr[0]
+    var obj2 = arr[1]
+    var ob = Observer.create(arr)
+    var binding = ob.binding
+    spyOn(binding, 'notify')
+    // remove by index
+    arr.$remove(0)
+    expect(arr.length).toBe(1)
+    expect(arr[0]).toBe(obj2)
+    expect(binding.notify.calls.count()).toBe(1)
+    // remove by identity, not in array
+    arr.$remove(obj1)
+    expect(arr.length).toBe(1)
+    expect(arr[0]).toBe(obj2)
+    expect(binding.notify.calls.count()).toBe(1)
+    // remove by identity, in array
+    arr.$remove(obj2)
+    expect(arr.length).toBe(0)
+    expect(binding.notify.calls.count()).toBe(2)
   })
 
 })