diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js
index cc82ea95a..2b964a548 100644
--- a/packages/create-instance/create-instance.js
+++ b/packages/create-instance/create-instance.js
@@ -6,6 +6,7 @@ import { addEventLogger } from './log-events'
import { createComponentStubs } from 'shared/stub-components'
import { throwError, warn, vueVersion } from 'shared/util'
import { compileTemplate } from 'shared/compile-template'
+import { isRequiredComponent } from 'shared/validators'
import extractInstanceOptions from './extract-instance-options'
import createFunctionalComponent from './create-functional-component'
import { componentNeedsCompiling, isPlainObject } from 'shared/validators'
@@ -64,8 +65,20 @@ export default function createInstance (
addEventLogger(_Vue)
+ // Replace globally registered components with components extended
+ // from localVue. This makes sure the beforeMount mixins to add stubs
+ // is applied to globally registered components.
+ // Vue version must be 2.3 or greater, because of a bug resolving
+ // extended constructor options (https://github.com/vuejs/vue/issues/4976)
+ if (vueVersion > 2.2) {
+ for (const c in _Vue.options.components) {
+ if (!isRequiredComponent(c)) {
+ _Vue.component(c, _Vue.extend(_Vue.options.components[c]))
+ }
+ }
+ }
+
const stubComponents = createComponentStubs(
- // $FlowIgnore
component.components,
// $FlowIgnore
options.stubs
@@ -73,17 +86,20 @@ export default function createInstance (
if (options.stubs) {
instanceOptions.components = {
...instanceOptions.components,
- // $FlowIgnore
...stubComponents
}
}
+ function addStubComponentsMixin () {
+ Object.assign(
+ this.$options.components,
+ stubComponents
+ )
+ }
_Vue.mixin({
- created () {
- Object.assign(
- this.$options.components,
- stubComponents
- )
- }
+ beforeMount: addStubComponentsMixin,
+ // beforeCreate is for components created in node, which
+ // never mount
+ beforeCreate: addStubComponentsMixin
})
Object.keys(componentOptions.components || {}).forEach(c => {
if (
diff --git a/packages/shared/stub-components.js b/packages/shared/stub-components.js
index 8fe7d3339..cd27a4f4a 100644
--- a/packages/shared/stub-components.js
+++ b/packages/shared/stub-components.js
@@ -11,7 +11,8 @@ import {
import {
componentNeedsCompiling,
templateContainsComponent,
- isVueComponent
+ isVueComponent,
+ isRequiredComponent
} from './validators'
import { compileTemplate } from './compile-template'
@@ -36,12 +37,6 @@ function resolveComponent (obj: Object, component: string): Object {
{}
}
-function isRequiredComponent (name): boolean {
- return (
- name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
- )
-}
-
function getCoreProperties (componentOptions: Component): Object {
return {
attrs: componentOptions.attrs,
diff --git a/packages/shared/validators.js b/packages/shared/validators.js
index 6116596d8..8259e753f 100644
--- a/packages/shared/validators.js
+++ b/packages/shared/validators.js
@@ -89,3 +89,9 @@ export function templateContainsComponent (
export function isPlainObject (obj: any): boolean {
return Object.prototype.toString.call(obj) === '[object Object]'
}
+
+export function isRequiredComponent (name: string): boolean {
+ return (
+ name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
+ )
+}
diff --git a/packages/test-utils/src/find-vue-components.js b/packages/test-utils/src/find-vue-components.js
index ff6b5d8f7..d9858c49b 100644
--- a/packages/test-utils/src/find-vue-components.js
+++ b/packages/test-utils/src/find-vue-components.js
@@ -76,7 +76,7 @@ export function vmFunctionalCtorMatchesSelector (
): boolean {
if (VUE_VERSION < 2.3) {
throwError(
- `find for functional components is not support in ` + `Vue < 2.3`
+ `find for functional components is not supported in ` + `Vue < 2.3`
)
}
diff --git a/test/specs/mounting-options/stubs.spec.js b/test/specs/mounting-options/stubs.spec.js
index 24a86cad8..7aa4b75d4 100644
--- a/test/specs/mounting-options/stubs.spec.js
+++ b/test/specs/mounting-options/stubs.spec.js
@@ -5,7 +5,7 @@ import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
import { createLocalVue, config } from '~vue/test-utils'
import { config as serverConfig } from '~vue/server-test-utils'
import Vue from 'vue'
-import { describeWithMountingMethods } from '~resources/utils'
+import { describeWithMountingMethods, vueVersion } from '~resources/utils'
import { itDoNotRunIf } from 'conditional-specs'
describeWithMountingMethods('options.stub', mountingMethod => {
@@ -133,19 +133,19 @@ describeWithMountingMethods('options.stub', mountingMethod => {
itDoNotRunIf(
mountingMethod.name === 'shallowMount',
'stubs nested components', () => {
- const GrandchildComponent = {
+ const GrandChildComponent = {
template: ''
}
const ChildComponent = {
- template: '',
- components: { GrandchildComponent }
+ template: '',
+ components: { GrandChildComponent }
}
const TestComponent = {
template: '',
components: { ChildComponent }
}
const wrapper = mountingMethod(TestComponent, {
- stubs: ['grandchild-component']
+ stubs: ['grand-child-component']
})
const HTML = mountingMethod.name === 'renderToString'
? wrapper
@@ -153,22 +153,50 @@ describeWithMountingMethods('options.stub', mountingMethod => {
expect(HTML).not.to.contain('')
})
+ itDoNotRunIf(
+ mountingMethod.name === 'shallowMount' || vueVersion < 2.3,
+ 'stubs nested components registered globally', () => {
+ const GrandChildComponent = {
+ render: h => h('span', ['hello'])
+ }
+ const ChildComponent = {
+ render: h => h('grand-child-component')
+ }
+ const TestComponent = {
+ render: h => h('child-component')
+ }
+ Vue.component('child-component', ChildComponent)
+ Vue.component('grand-child-component', GrandChildComponent)
+
+ const wrapper = mountingMethod(TestComponent, {
+ stubs: {
+ 'grand-child-component': true
+ }
+ })
+ const HTML = mountingMethod.name === 'renderToString'
+ ? wrapper
+ : wrapper.html()
+ expect(HTML).not.to.contain('')
+ delete Vue.options.components['child-component']
+ delete Vue.options.components['grand-child-component']
+ })
+
itDoNotRunIf(
mountingMethod.name === 'shallowMount',
'stubs nested components on extended components', () => {
- const GrandchildComponent = {
+ const GrandChildComponent = {
template: ''
}
const ChildComponent = {
- template: '',
- components: { GrandchildComponent }
+ template: '',
+ components: { GrandChildComponent }
}
const TestComponent = {
template: '
',
components: { ChildComponent }
}
const wrapper = mountingMethod(Vue.extend(TestComponent), {
- stubs: ['grandchild-component']
+ stubs: ['grand-child-component']
})
const HTML = mountingMethod.name === 'renderToString'
? wrapper
diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js
index 717b34821..a45e8fc53 100644
--- a/test/specs/wrapper/find.spec.js
+++ b/test/specs/wrapper/find.spec.js
@@ -186,7 +186,7 @@ describeWithShallowAndMount('find', mountingMethod => {
const wrapper = mountingMethod(TestComponent)
if (vueVersion < 2.3) {
const message =
- '[vue-test-utils]: find for functional components is not support in Vue < 2.3'
+ '[vue-test-utils]: find for functional components is not supported in Vue < 2.3'
const fn = () => wrapper.find(TestFunctionalComponent)
expect(fn)
.to.throw()