Bläddra i källkod

restructure global api tests + ensure global mixins work regardless of constructor creation time

Evan You 10 år sedan
förälder
incheckning
8c8eaea0fb

+ 10 - 2
src/core/config.js

@@ -9,7 +9,8 @@ export type Config = {
   _assetTypes: Array<string>,
   _lifecycleHooks: Array<string>,
   _maxUpdateCount: number,
-  _isServer: boolean
+  _isServer: boolean,
+  _ctors: Array<Function>
 }
 
 const config: Config = {
@@ -73,7 +74,14 @@ const config: Config = {
   /**
    * Server rendering?
    */
-  _isServer: process.env.VUE_ENV === 'server'
+  _isServer: process.env.VUE_ENV === 'server',
+
+  /**
+   * Keeping track of all extended Component constructors
+   * so that we can update them in the case of global mixins being applied
+   * after their creation.
+   */
+  _ctors: []
 }
 
 export default config

+ 7 - 1
src/core/global-api/extend.js

@@ -1,7 +1,7 @@
 /* @flow */
 
 import config from '../config'
-import { warn, mergeOptions } from '../util/index'
+import { warn, remove, mergeOptions } from '../util/index'
 
 export function initExtend (Vue: GlobalAPI) {
   /**
@@ -54,6 +54,12 @@ export function initExtend (Vue: GlobalAPI) {
     if (name) {
       Sub.options.components[name] = Sub
     }
+    // book-keeping for global mixin edge cases. also expose a way to remove it
+    Sub.extendOptions = extendOptions
+    config._ctors.push(Sub)
+    Sub.release = () => {
+      remove(config._ctors, Sub)
+    }
     // cache constructor
     if (isFirstExtend) {
       extendOptions._Ctor = Sub

+ 5 - 0
src/core/global-api/mixin.js

@@ -1,9 +1,14 @@
 /* @flow */
 
+import config from '../config'
 import { mergeOptions } from '../util/index'
 
 export function initMixin (Vue: GlobalAPI) {
   Vue.mixin = function (mixin: Object) {
     Vue.options = mergeOptions(Vue.options, mixin)
+    // update constructors that are already created
+    config._ctors.forEach(Ctor => {
+      Ctor.options = mergeOptions(Ctor['super'].options, Ctor.extendOptions)
+    })
   }
 }

+ 1 - 1
test/unit/features/global-api/global-assets-api.spec.js → test/unit/features/global-api/assets.spec.js

@@ -1,6 +1,6 @@
 import Vue from 'vue'
 
-describe('Global Assets API', () => {
+describe('Global API: assets', () => {
   const Test = Vue.extend()
 
   it('directive / transition / filters', () => {

+ 15 - 0
test/unit/features/global-api/compile.spec.js

@@ -0,0 +1,15 @@
+import Vue from 'vue'
+
+describe('Global API: compile', () => {
+  it('should compile render functions', () => {
+    const res = Vue.compile('<div><span>{{ msg }}</span></div>')
+    const vm = new Vue({
+      data: {
+        msg: 'hello'
+      },
+      render: res.render,
+      staticRenderFns: res.staticRenderFns
+    }).$mount()
+    expect(vm.$el.innerHTML).toContain('<span>hello</span>')
+  })
+})

+ 0 - 0
test/unit/features/global-api/global-config.spec.js → test/unit/features/global-api/config.spec.js


+ 53 - 0
test/unit/features/global-api/extend.spec.js

@@ -0,0 +1,53 @@
+import Vue from 'vue'
+
+describe('Global API: extend', () => {
+  it('should correctly merge options', () => {
+    const Test = Vue.extend({
+      name: 'test',
+      a: 1,
+      b: 2
+    })
+    expect(Test.options.a).toBe(1)
+    expect(Test.options.b).toBe(2)
+    expect(Test.super).toBe(Vue)
+    const t = new Test({
+      a: 2
+    })
+    expect(t.$options.a).toBe(2)
+    expect(t.$options.b).toBe(2)
+    // inheritance
+    const Test2 = Test.extend({
+      a: 2
+    })
+    expect(Test2.options.a).toBe(2)
+    expect(Test2.options.b).toBe(2)
+    const t2 = new Test2({
+      a: 3
+    })
+    expect(t2.$options.a).toBe(3)
+    expect(t2.$options.b).toBe(2)
+  })
+
+  it('should warn invalid names', () => {
+    Vue.extend({ name: '123' })
+    expect('Invalid component name: "123"').toHaveBeenWarned()
+    Vue.extend({ name: '_fesf' })
+    expect('Invalid component name: "_fesf"').toHaveBeenWarned()
+    Vue.extend({ name: 'Some App' })
+    expect('Invalid component name: "Some App"').toHaveBeenWarned()
+  })
+
+  it('should work when used as components', () => {
+    const foo = Vue.extend({
+      template: '<span>foo</span>'
+    })
+    const bar = Vue.extend({
+      template: '<span>bar</span>'
+    })
+    const vm = new Vue({
+      template: '<div><foo></foo><bar></bar></div>',
+      components: { foo, bar }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
+  })
+})

+ 0 - 98
test/unit/features/global-api/global-api.spec.js

@@ -1,98 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API', () => {
-  it('extend', () => {
-    const Test = Vue.extend({
-      name: 'test',
-      a: 1,
-      b: 2
-    })
-    expect(Test.options.a).toBe(1)
-    expect(Test.options.b).toBe(2)
-    expect(Test.super).toBe(Vue)
-    const t = new Test({
-      a: 2
-    })
-    expect(t.$options.a).toBe(2)
-    expect(t.$options.b).toBe(2)
-    // inheritance
-    const Test2 = Test.extend({
-      a: 2
-    })
-    expect(Test2.options.a).toBe(2)
-    expect(Test2.options.b).toBe(2)
-    const t2 = new Test2({
-      a: 3
-    })
-    expect(t2.$options.a).toBe(3)
-    expect(t2.$options.b).toBe(2)
-  })
-
-  it('extend warn invalid names', () => {
-    Vue.extend({ name: '123' })
-    expect('Invalid component name: "123"').toHaveBeenWarned()
-    Vue.extend({ name: '_fesf' })
-    expect('Invalid component name: "_fesf"').toHaveBeenWarned()
-    Vue.extend({ name: 'Some App' })
-    expect('Invalid component name: "Some App"').toHaveBeenWarned()
-  })
-
-  it('Vue.extend works', () => {
-    const foo = Vue.extend({
-      template: '<span>foo</span>'
-    })
-    const bar = Vue.extend({
-      template: '<span>bar</span>'
-    })
-    const vm = new Vue({
-      template: '<div><foo></foo><bar></bar></div>',
-      components: { foo, bar }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
-  })
-
-  it('global mixin', () => {
-    const options = Vue.options
-    const spy = jasmine.createSpy('global mixin')
-    Vue.mixin({
-      created: function () {
-        spy(this.$options.myOption)
-      }
-    })
-    new Vue({
-      myOption: 'hello'
-    })
-    expect(spy).toHaveBeenCalledWith('hello')
-    Vue.options = options
-  })
-
-  it('use', () => {
-    const def = {}
-    const options = {}
-    const pluginStub = {
-      install: (Vue, opts) => {
-        Vue.directive('plugin-test', def)
-        expect(opts).toBe(options)
-      }
-    }
-    Vue.use(pluginStub, options)
-    expect(Vue.options.directives['plugin-test']).toBe(def)
-    delete Vue.options.directives['plugin-test']
-    // use a function
-    Vue.use(pluginStub.install, options)
-    expect(Vue.options.directives['plugin-test']).toBe(def)
-    delete Vue.options.directives['plugin-test']
-  })
-
-  it('compile', () => {
-    const res = Vue.compile('<div><span>{{ msg }}</span></div>')
-    const vm = new Vue({
-      data: {
-        msg: 'hello'
-      },
-      render: res.render,
-      staticRenderFns: res.staticRenderFns
-    }).$mount()
-    expect(vm.$el.innerHTML).toContain('<span>hello</span>')
-  })
-})

+ 45 - 0
test/unit/features/global-api/mixin.spec.js

@@ -0,0 +1,45 @@
+import Vue from 'vue'
+
+describe('Global API: mixin', () => {
+  let options
+  beforeEach(() => { options = Vue.options })
+  afterEach(() => { Vue.options = options })
+
+  it('should work', () => {
+    const spy = jasmine.createSpy('global mixin')
+    Vue.mixin({
+      created () {
+        spy(this.$options.myOption)
+      }
+    })
+    new Vue({
+      myOption: 'hello'
+    })
+    expect(spy).toHaveBeenCalledWith('hello')
+  })
+
+  it('should work for constructors created before mixin is applied', () => {
+    const calls = []
+    const Test = Vue.extend({
+      init () {
+        calls.push(this.$options.myOption + ' local')
+      }
+    })
+    Vue.mixin({
+      init () {
+        calls.push(this.$options.myOption + ' global')
+      }
+    })
+    new Test({
+      myOption: 'hello'
+    })
+    expect(calls).toEqual(['hello global', 'hello local'])
+  })
+
+  it('should allow releasing constructors', () => {
+    const Test = Vue.extend({})
+    expect(Vue.config._ctors.indexOf(Test) > -1).toBe(true)
+    Test.release()
+    expect(Vue.config._ctors.indexOf(Test) > -1).toBe(false)
+  })
+})

+ 1 - 1
test/unit/features/global-api/global-observer-api.spec.js → test/unit/features/global-api/set-delete.spec.js

@@ -1,6 +1,6 @@
 import Vue from 'vue'
 
-describe('Global Data Observer API', () => {
+describe('Global API: set/delete', () => {
   describe('Vue.set', () => {
     it('should update a vue object', done => {
       const vm = new Vue({

+ 24 - 0
test/unit/features/global-api/use.spec.js

@@ -0,0 +1,24 @@
+import Vue from 'vue'
+
+describe('Global API: use', () => {
+  const def = {}
+  const options = {}
+  const pluginStub = {
+    install: (Vue, opts) => {
+      Vue.directive('plugin-test', def)
+      expect(opts).toBe(options)
+    }
+  }
+
+  it('should apply Object plugin', () => {
+    Vue.use(pluginStub, options)
+    expect(Vue.options.directives['plugin-test']).toBe(def)
+    delete Vue.options.directives['plugin-test']
+  })
+
+  it('should apply Function plugin', () => {
+    Vue.use(pluginStub.install, options)
+    expect(Vue.options.directives['plugin-test']).toBe(def)
+    delete Vue.options.directives['plugin-test']
+  })
+})