Skip to content

Commit 5af3677

Browse files
authored
fix: stub globally registered components (#859)
1 parent 1c8109d commit 5af3677

File tree

6 files changed

+71
-26
lines changed

6 files changed

+71
-26
lines changed

Diff for: packages/create-instance/create-instance.js

+24-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { addEventLogger } from './log-events'
66
import { createComponentStubs } from 'shared/stub-components'
77
import { throwError, warn, vueVersion } from 'shared/util'
88
import { compileTemplate } from 'shared/compile-template'
9+
import { isRequiredComponent } from 'shared/validators'
910
import extractInstanceOptions from './extract-instance-options'
1011
import createFunctionalComponent from './create-functional-component'
1112
import { componentNeedsCompiling, isPlainObject } from 'shared/validators'
@@ -64,26 +65,41 @@ export default function createInstance (
6465

6566
addEventLogger(_Vue)
6667

68+
// Replace globally registered components with components extended
69+
// from localVue. This makes sure the beforeMount mixins to add stubs
70+
// is applied to globally registered components.
71+
// Vue version must be 2.3 or greater, because of a bug resolving
72+
// extended constructor options (https://github.com/vuejs/vue/issues/4976)
73+
if (vueVersion > 2.2) {
74+
for (const c in _Vue.options.components) {
75+
if (!isRequiredComponent(c)) {
76+
_Vue.component(c, _Vue.extend(_Vue.options.components[c]))
77+
}
78+
}
79+
}
80+
6781
const stubComponents = createComponentStubs(
68-
// $FlowIgnore
6982
component.components,
7083
// $FlowIgnore
7184
options.stubs
7285
)
7386
if (options.stubs) {
7487
instanceOptions.components = {
7588
...instanceOptions.components,
76-
// $FlowIgnore
7789
...stubComponents
7890
}
7991
}
92+
function addStubComponentsMixin () {
93+
Object.assign(
94+
this.$options.components,
95+
stubComponents
96+
)
97+
}
8098
_Vue.mixin({
81-
created () {
82-
Object.assign(
83-
this.$options.components,
84-
stubComponents
85-
)
86-
}
99+
beforeMount: addStubComponentsMixin,
100+
// beforeCreate is for components created in node, which
101+
// never mount
102+
beforeCreate: addStubComponentsMixin
87103
})
88104
Object.keys(componentOptions.components || {}).forEach(c => {
89105
if (

Diff for: packages/shared/stub-components.js

+2-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
import {
1212
componentNeedsCompiling,
1313
templateContainsComponent,
14-
isVueComponent
14+
isVueComponent,
15+
isRequiredComponent
1516
} from './validators'
1617
import { compileTemplate } from './compile-template'
1718

@@ -36,12 +37,6 @@ function resolveComponent (obj: Object, component: string): Object {
3637
{}
3738
}
3839

39-
function isRequiredComponent (name): boolean {
40-
return (
41-
name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
42-
)
43-
}
44-
4540
function getCoreProperties (componentOptions: Component): Object {
4641
return {
4742
attrs: componentOptions.attrs,

Diff for: packages/shared/validators.js

+6
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,9 @@ export function templateContainsComponent (
8989
export function isPlainObject (obj: any): boolean {
9090
return Object.prototype.toString.call(obj) === '[object Object]'
9191
}
92+
93+
export function isRequiredComponent (name: string): boolean {
94+
return (
95+
name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
96+
)
97+
}

Diff for: packages/test-utils/src/find-vue-components.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export function vmFunctionalCtorMatchesSelector (
7676
): boolean {
7777
if (VUE_VERSION < 2.3) {
7878
throwError(
79-
`find for functional components is not support in ` + `Vue < 2.3`
79+
`find for functional components is not supported in ` + `Vue < 2.3`
8080
)
8181
}
8282

Diff for: test/specs/mounting-options/stubs.spec.js

+37-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
55
import { createLocalVue, config } from '~vue/test-utils'
66
import { config as serverConfig } from '~vue/server-test-utils'
77
import Vue from 'vue'
8-
import { describeWithMountingMethods } from '~resources/utils'
8+
import { describeWithMountingMethods, vueVersion } from '~resources/utils'
99
import { itDoNotRunIf } from 'conditional-specs'
1010

1111
describeWithMountingMethods('options.stub', mountingMethod => {
@@ -133,42 +133,70 @@ describeWithMountingMethods('options.stub', mountingMethod => {
133133
itDoNotRunIf(
134134
mountingMethod.name === 'shallowMount',
135135
'stubs nested components', () => {
136-
const GrandchildComponent = {
136+
const GrandChildComponent = {
137137
template: '<span />'
138138
}
139139
const ChildComponent = {
140-
template: '<grandchild-component />',
141-
components: { GrandchildComponent }
140+
template: '<grand-child-component />',
141+
components: { GrandChildComponent }
142142
}
143143
const TestComponent = {
144144
template: '<child-component />',
145145
components: { ChildComponent }
146146
}
147147
const wrapper = mountingMethod(TestComponent, {
148-
stubs: ['grandchild-component']
148+
stubs: ['grand-child-component']
149149
})
150150
const HTML = mountingMethod.name === 'renderToString'
151151
? wrapper
152152
: wrapper.html()
153153
expect(HTML).not.to.contain('<span>')
154154
})
155155

156+
itDoNotRunIf(
157+
mountingMethod.name === 'shallowMount' || vueVersion < 2.3,
158+
'stubs nested components registered globally', () => {
159+
const GrandChildComponent = {
160+
render: h => h('span', ['hello'])
161+
}
162+
const ChildComponent = {
163+
render: h => h('grand-child-component')
164+
}
165+
const TestComponent = {
166+
render: h => h('child-component')
167+
}
168+
Vue.component('child-component', ChildComponent)
169+
Vue.component('grand-child-component', GrandChildComponent)
170+
171+
const wrapper = mountingMethod(TestComponent, {
172+
stubs: {
173+
'grand-child-component': true
174+
}
175+
})
176+
const HTML = mountingMethod.name === 'renderToString'
177+
? wrapper
178+
: wrapper.html()
179+
expect(HTML).not.to.contain('<span>')
180+
delete Vue.options.components['child-component']
181+
delete Vue.options.components['grand-child-component']
182+
})
183+
156184
itDoNotRunIf(
157185
mountingMethod.name === 'shallowMount',
158186
'stubs nested components on extended components', () => {
159-
const GrandchildComponent = {
187+
const GrandChildComponent = {
160188
template: '<span />'
161189
}
162190
const ChildComponent = {
163-
template: '<grandchild-component />',
164-
components: { GrandchildComponent }
191+
template: '<grand-child-component />',
192+
components: { GrandChildComponent }
165193
}
166194
const TestComponent = {
167195
template: '<div><child-component /></div>',
168196
components: { ChildComponent }
169197
}
170198
const wrapper = mountingMethod(Vue.extend(TestComponent), {
171-
stubs: ['grandchild-component']
199+
stubs: ['grand-child-component']
172200
})
173201
const HTML = mountingMethod.name === 'renderToString'
174202
? wrapper

Diff for: test/specs/wrapper/find.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ describeWithShallowAndMount('find', mountingMethod => {
186186
const wrapper = mountingMethod(TestComponent)
187187
if (vueVersion < 2.3) {
188188
const message =
189-
'[vue-test-utils]: find for functional components is not support in Vue < 2.3'
189+
'[vue-test-utils]: find for functional components is not supported in Vue < 2.3'
190190
const fn = () => wrapper.find(TestFunctionalComponent)
191191
expect(fn)
192192
.to.throw()

0 commit comments

Comments
 (0)