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  })