Browse Source

improve test coverage

Evan You 11 years ago
parent
commit
02ee11cac1

+ 1 - 1
src/api/child.js

@@ -26,7 +26,7 @@ exports.$addChild = function (opts, BaseCtor) {
     if (!ChildVue) {
       ChildVue = function (options) {
         this.$parent = parent
-        this.$root = parent.$root || parent
+        this.$root = parent.$root
         this.constructor = ChildVue
         _.Vue.call(this, options)
       }

+ 0 - 2
src/compile/transclude.js

@@ -21,8 +21,6 @@ module.exports = function transclude (el, options) {
     if (!el) {
       _.warn('Cannot find element: ' + selector)
     }
-  } else if (type === 'function') {
-    el = el()
   }
   if (el instanceof DocumentFragment) {
     return transcludeBlock(el)

+ 47 - 29
src/util/merge-option.js

@@ -20,27 +20,56 @@ var strats = {}
  */
 
 strats.data = function (parentVal, childVal, vm) {
-  if (!childVal) return parentVal
-  if (!parentVal || !vm) {
-    return childVal
-  }
-  // instance option is a function, just call it here.
-  if (typeof childVal === 'function') {
-    childVal = childVal()
+  // in a class merge, both should be functions
+  // so we just return child if it exists
+  if (!vm) {
+    if (childVal && typeof childVal !== 'function') {
+      _.warn(
+        'The "data" option should be a function ' +
+        'that returns a per-instance value in component ' +
+        'definitions.'
+      )
+      return
+    }
+    return childVal || parentVal
   }
-  // the special case where parent data is a function,
-  // and instance also has passed-in data. we need to mix
-  // the default data returned from the function into the
-  // passed-in one.
-  if (typeof parentVal === 'function') {
-    var defaultData = parentVal()
+  var instanceData = typeof childVal === 'function'
+    ? childVal()
+    : childVal
+  var defaultData = typeof parentVal === 'function'
+    ? parentVal()
+    : undefined
+  if (instanceData) {
+    // mix default data into instance data
     for (var key in defaultData) {
-      if (!childVal.hasOwnProperty(key)) {
-        childVal[key] = defaultData[key]
+      if (!instanceData.hasOwnProperty(key)) {
+        instanceData[key] = defaultData[key]
       }
     }
+    return instanceData
+  } else {
+    return defaultData
   }
-  return childVal
+}
+
+/**
+ * El
+ */
+
+strats.el = function (parentVal, childVal, vm) {
+  if (!vm && childVal && typeof childVal !== 'function') {
+    _.warn(
+      'The "el" option should be a function ' +
+      'that returns a per-instance value in component ' +
+      'definitions.'
+    )
+    return
+  }
+  var ret = childVal || parentVal
+  // invoke the element factory if this is instance merge
+  return vm && typeof ret === 'function'
+    ? ret()
+    : ret
 }
 
 /**
@@ -178,19 +207,8 @@ module.exports = function mergeOptions (parent, child, vm) {
     }
   }
   function merge (key) {
-    if (
-      !vm &&
-      (key === 'el' || key === 'data') &&
-      typeof child[key] !== 'function') {
-      _.warn(
-        'The "' + key + '" option should be a function ' +
-        'that returns a per-instance value in component ' +
-        'definitions.'
-      )
-    } else {
-      var strat = strats[key] || defaultStrat
-      options[key] = strat(parent[key], child[key], vm, key)
-    }
+    var strat = strats[key] || defaultStrat
+    options[key] = strat(parent[key], child[key], vm, key)
   }
   return options
 }

+ 4 - 0
test/unit/specs/api/events_spec.js

@@ -72,7 +72,11 @@ describe('Events API', function () {
   it('$broadcast optimization', function () {
     var child = vm.$addChild()
     var child2 = child.$addChild()
+    // hooks should not incurr the bookkeeping cost
+    child.$on('hook:created', function () {})
+    expect(vm._eventsCount['hook:created']).toBeUndefined()
     child.$on('test', spy)
+    expect(vm._eventsCount['test']).toBe(1)
     // child2's $emit & $broadcast
     // shouldn't get called if no child listens to the event
     child2.$emit = spy

+ 29 - 0
test/unit/specs/directive_spec.js

@@ -136,4 +136,33 @@ describe('Directive', function () {
     })
   })
 
+  it('function def', function () {
+    var d = new Directive('test', el, vm, {
+      expression: 'a'
+    }, def.update)
+    expect(d.update).toBe(def.update)
+    expect(def.update).toHaveBeenCalled()
+  })
+
+  it('reuse the same watcher', function (done) {
+    var d = new Directive('test', el, vm, {
+      expression: 'a',
+    }, def)
+    var d2 = new Directive('test', el, vm, {
+      expression: 'a',
+    }, def)
+    expect(vm._watcherList.length).toBe(1)
+    expect(d._watcher).toBe(d2._watcher)
+    d2._teardown()
+    expect(d2._watcher).toBeNull()
+    expect(vm._watcherList.length).toBe(1)
+    vm.a = 2
+    nextTick(function () {
+      expect(def.update).toHaveBeenCalledWith(2, 1)
+      d._teardown()
+      expect(vm._watcherList.length).toBe(0)
+      done()
+    })
+  })
+
 })

+ 21 - 0
test/unit/specs/util/dom_spec.js

@@ -17,6 +17,14 @@ if (_.inBrowser) {
       parent.appendChild(child) 
     })
 
+    it('inDoc', function () {
+      expect(_.inDoc(target)).toBe(false)
+      document.body.appendChild(target)
+      expect(_.inDoc(target)).toBe(true)
+      document.body.removeChild(target)
+      expect(_.inDoc(target)).toBe(false)
+    })
+
     it('attr', function () {
       target.setAttribute('v-test', 'ok')
       var val = _.attr(target, 'test')
@@ -77,5 +85,18 @@ if (_.inBrowser) {
       expect(target.getAttribute('test1')).toBe('1')
       expect(target.getAttribute('test2')).toBe('2')
     })
+
+    it('on/off', function () {
+      var spy = jasmine.createSpy()
+      _.on(target, 'click', spy)
+      var e = document.createEvent('HTMLEvents')
+      e.initEvent('click', true, true)
+      target.dispatchEvent(e)
+      expect(spy.calls.count()).toBe(1)
+      expect(spy).toHaveBeenCalledWith(e)
+      _.off(target, 'click', spy)
+      target.dispatchEvent(e)
+      expect(spy.calls.count()).toBe(1)
+    })
   })
 }

+ 51 - 7
test/unit/specs/util/merge-option_spec.js

@@ -37,6 +37,13 @@ describe('Util - Option merging', function () {
 
   it('events', function () {
 
+    // no parent
+    res = merge({}, {events:1})
+    expect(res.events).toBe(1)
+    // no child
+    res = merge({events:1}, {})
+    expect(res.events).toBe(1)
+
     var fn1 = function () {}
     var fn2 = function () {}
     var fn3 = function () {}
@@ -65,7 +72,6 @@ describe('Util - Option merging', function () {
         expect(res[i]).toBe(expected[i])
       }
     }
-
   })
 
   it('normal object hashes', function () {
@@ -123,13 +129,53 @@ describe('Util - Option merging', function () {
     expect(res.components.a.super).toBe(Vue)
   })
 
-  it('should ignore el & data in class merge', function () {
+  it('should ignore non-function el & data in class merge', function () {
     var res = merge({}, {el:1, data:2})
     expect(res.el).toBeUndefined()
     expect(res.data).toBeUndefined()
   })
 
-  it('data merge with default data function', function () {
+  it('class data/el merge', function () {
+    function fn1 () {}
+    function fn2 () {}
+    var res = merge({data:fn1, el:fn1}, {data:fn2})
+    expect(res.data).toBe(fn2)
+    expect(res.el).toBe(fn1)
+  })
+
+  it('instanace el merge', function () {
+    function fn1 () {
+      return 1
+    }
+    function fn2 () {
+      return 2
+    }
+    // both functions
+    var res = merge({el:fn1}, {el:fn2}, {})
+    expect(res.el).toBe(2)
+    // direct instance el
+    res = merge({el:fn1}, {el:2}, {})
+    expect(res.el).toBe(2)
+    // no parent
+    res = merge({}, {el:2}, {})
+    expect(res.el).toBe(2)
+    // no child
+    res = merge({el:fn1}, {}, {})
+    expect(res.el).toBe(1)
+  })
+
+  it('instance data merge with no instance data', function () {
+    var res = merge(
+      {data: function () {
+        return { a: 1}
+      }},
+      {}, // no instance data
+      {} // mock vm presence
+    )
+    expect(res.data.a).toBe(1)
+  })
+
+  it('instance data merge with default data function', function () {
     var res = merge(
       // component default
       { data: function () {
@@ -138,10 +184,8 @@ describe('Util - Option merging', function () {
           b: 2
         }
       }},
-      // instance data
-      { data: { a: 2 }},
-      // mock vm presence
-      {}
+      { data: { a: 2 }}, // instance data
+      {} // mock vm presence
     )
     expect(res.data.a).toBe(2)
     expect(res.data.b).toBe(2)

+ 22 - 1
test/unit/specs/watcher_spec.js

@@ -114,11 +114,25 @@ describe('Watcher', function () {
     var watcher2 = new Watcher(vm, 'b.e', spy)
     expect(watcher.value).toBeUndefined()
     expect(watcher2.value).toBeUndefined()
+    // check $add affecting children
+    var child = vm.$addChild()
+    var watcher3 = new Watcher(child, 'd.e', spy)
+    var watcher4 = new Watcher(child, 'b.e', spy)
+    // check $add should not affect isolated children
+    var child2 = vm.$addChild({
+      isolated: true
+    })
+    var watcher5 = new Watcher(child2, 'd.e', spy)
+    expect(watcher5.value).toBeUndefined()
     vm.$add('d', { e: 123 })
     vm.b.$add('e', 234)
     nextTick(function () {
       expect(watcher.value).toBe(123)
       expect(watcher2.value).toBe(234)
+      expect(watcher3.value).toBe(123)
+      expect(watcher4.value).toBe(234)
+      expect(watcher5.value).toBeUndefined()
+      expect(spy.calls.count()).toBe(4)
       expect(spy).toHaveBeenCalledWith(123, undefined)
       expect(spy).toHaveBeenCalledWith(234, undefined)
       done()
@@ -137,12 +151,19 @@ describe('Watcher', function () {
   })
 
   it('swapping $data', function (done) {
+    // existing path
     var watcher = new Watcher(vm, 'b.c', spy)
+    var spy2 = jasmine.createSpy()
+    // non-existing path
+    var watcher2 = new Watcher(vm, 'e', spy2)
     expect(watcher.value).toBe(2)
-    vm.$data = { b: { c: 3}}
+    expect(watcher2.value).toBeUndefined()
+    vm.$data = { b: { c: 3}, e: 4 }
     nextTick(function () {
       expect(watcher.value).toBe(3)
+      expect(watcher2.value).toBe(4)
       expect(spy).toHaveBeenCalledWith(3, 2)
+      expect(spy2).toHaveBeenCalledWith(4, undefined)
       done()
     })
   })