Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: stub globally registered components #859

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -64,26 +65,41 @@ 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
)
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 (
Expand Down
9 changes: 2 additions & 7 deletions packages/shared/stub-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
import {
componentNeedsCompiling,
templateContainsComponent,
isVueComponent
isVueComponent,
isRequiredComponent
} from './validators'
import { compileTemplate } from './compile-template'

Expand All @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
)
}
2 changes: 1 addition & 1 deletion packages/test-utils/src/find-vue-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
)
}

Expand Down
46 changes: 37 additions & 9 deletions test/specs/mounting-options/stubs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -133,42 +133,70 @@ describeWithMountingMethods('options.stub', mountingMethod => {
itDoNotRunIf(
mountingMethod.name === 'shallowMount',
'stubs nested components', () => {
const GrandchildComponent = {
const GrandChildComponent = {
template: '<span />'
}
const ChildComponent = {
template: '<grandchild-component />',
components: { GrandchildComponent }
template: '<grand-child-component />',
components: { GrandChildComponent }
}
const TestComponent = {
template: '<child-component />',
components: { ChildComponent }
}
const wrapper = mountingMethod(TestComponent, {
stubs: ['grandchild-component']
stubs: ['grand-child-component']
})
const HTML = mountingMethod.name === 'renderToString'
? wrapper
: wrapper.html()
expect(HTML).not.to.contain('<span>')
})

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('<span>')
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: '<span />'
}
const ChildComponent = {
template: '<grandchild-component />',
components: { GrandchildComponent }
template: '<grand-child-component />',
components: { GrandChildComponent }
}
const TestComponent = {
template: '<div><child-component /></div>',
components: { ChildComponent }
}
const wrapper = mountingMethod(Vue.extend(TestComponent), {
stubs: ['grandchild-component']
stubs: ['grand-child-component']
})
const HTML = mountingMethod.name === 'renderToString'
? wrapper
Expand Down
2 changes: 1 addition & 1 deletion test/specs/wrapper/find.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down