diff --git a/docs/api/mount.md b/docs/api/mount.md
index 6744186e5..240c15b97 100644
--- a/docs/api/mount.md
+++ b/docs/api/mount.md
@@ -116,9 +116,9 @@ describe('Foo', () => {
it('renders a div', () => {
const wrapper = mount(Foo, {
stubs: {
- Bar: '
',
BarFoo: true,
- FooBar: Faz
+ FooBar: Faz,
+ Bar: { template: '' }
}
})
expect(wrapper.contains('.stubbed')).toBe(true)
@@ -127,4 +127,8 @@ describe('Foo', () => {
})
```
+**Deprecation Notice:**
+
+When stubbing components, supplying a string (`ComponentToStub: '`) is no longer supported.
+
- **See also:** [Wrapper](wrapper/)
diff --git a/docs/api/options.md b/docs/api/options.md
index 1704ed31b..f3b13fce5 100644
--- a/docs/api/options.md
+++ b/docs/api/options.md
@@ -208,9 +208,13 @@ const wrapper = mount(WrapperComp).find(ComponentUnderTest)
## stubs
-- type: `{ [name: string]: Component | boolean } | Array`
+- type: `{ [name: string]: Component | string | boolean } | Array`
-Stubs child components. Can be an Array of component names to stub, or an object. If `stubs` is an Array, every stub is `<${component name}-stub>`.
+Stubs child components can be an Array of component names to stub, or an object. If `stubs` is an Array, every stub is `<${component name}-stub>`.
+
+**Deprecation Notice:**
+
+When stubbing components, supplying a string (`ComponentToStub: '`) is no longer supported.
Example:
diff --git a/packages/create-instance/create-component-stubs.js b/packages/create-instance/create-component-stubs.js
index b82addd99..61dd0ae5d 100644
--- a/packages/create-instance/create-component-stubs.js
+++ b/packages/create-instance/create-component-stubs.js
@@ -6,7 +6,8 @@ import {
camelize,
capitalize,
hyphenate,
- keys
+ keys,
+ warn
} from '../shared/util'
import {
componentNeedsCompiling,
@@ -15,7 +16,7 @@ import {
isDynamicComponent,
isConstructor
} from '../shared/validators'
-import { compileTemplate, compileFromString } from '../shared/compile-template'
+import { compileTemplate } from '../shared/compile-template'
function isVueComponentStub(comp): boolean {
return (comp && comp.template) || isVueComponent(comp)
@@ -145,24 +146,31 @@ export function createStubFromComponent(
}
}
-function createStubFromString(
- templateString: string,
- originalComponent: Component = {},
- name: string,
- _Vue: Component
-): Component {
+// DEPRECATED: converts string stub to template stub.
+function createStubFromString(templateString: string, name: string): Component {
+ warn('String stubs are deprecated and will be removed in future versions')
+
if (templateContainsComponent(templateString, name)) {
throwError('options.stub cannot contain a circular reference')
}
- const componentOptions = resolveOptions(originalComponent, _Vue)
return {
- ...getCoreProperties(componentOptions),
- $_doNotStubChildren: true,
- ...compileFromString(templateString)
+ template: templateString,
+ $_doNotStubChildren: true
}
}
+function setStubComponentName(
+ stub: Object,
+ originalComponent: Component = {},
+ _Vue: Component
+) {
+ if (stub.name) return
+
+ const componentOptions = resolveOptions(originalComponent, _Vue)
+ stub.name = getCoreProperties(componentOptions).name
+}
+
function validateStub(stub) {
if (!isValidStub(stub)) {
throwError(`options.stub values must be passed a string or ` + `component`)
@@ -175,7 +183,7 @@ export function createStubsFromStubsObject(
_Vue: Component
): Components {
return Object.keys(stubs || {}).reduce((acc, stubName) => {
- const stub = stubs[stubName]
+ let stub = stubs[stubName]
validateStub(stub)
@@ -183,18 +191,19 @@ export function createStubsFromStubsObject(
return acc
}
+ const component = resolveComponent(originalComponents, stubName)
+
if (stub === true) {
- const component = resolveComponent(originalComponents, stubName)
acc[stubName] = createStubFromComponent(component, stubName, _Vue)
return acc
}
if (typeof stub === 'string') {
- const component = resolveComponent(originalComponents, stubName)
- acc[stubName] = createStubFromString(stub, component, stubName, _Vue)
- return acc
+ stub = createStubFromString(stub, stubName)
+ stubs[stubName]
}
+ setStubComponentName(stub, component, _Vue)
if (componentNeedsCompiling(stub)) {
compileTemplate(stub)
}
diff --git a/packages/shared/compile-template.js b/packages/shared/compile-template.js
index 7999894bc..49bbdeb91 100644
--- a/packages/shared/compile-template.js
+++ b/packages/shared/compile-template.js
@@ -4,19 +4,16 @@ import { compileToFunctions } from 'vue-template-compiler'
import { componentNeedsCompiling } from './validators'
import { throwError } from './util'
-export function compileFromString(str: string) {
- if (!compileToFunctions) {
- throwError(
- `vueTemplateCompiler is undefined, you must pass ` +
- `precompiled components if vue-template-compiler is ` +
- `undefined`
- )
- }
- return compileToFunctions(str)
-}
-
export function compileTemplate(component: Component): void {
if (component.template) {
+ if (!compileToFunctions) {
+ throwError(
+ `vueTemplateCompiler is undefined, you must pass ` +
+ `precompiled components if vue-template-compiler is ` +
+ `undefined`
+ )
+ }
+
if (component.template.charAt('#') === '#') {
var el = document.querySelector(component.template)
if (!el) {
@@ -27,7 +24,10 @@ export function compileTemplate(component: Component): void {
component.template = el.innerHTML
}
- Object.assign(component, compileToFunctions(component.template))
+ Object.assign(component, {
+ ...compileToFunctions(component.template),
+ name: component.name
+ })
}
if (component.components) {
diff --git a/test/resources/components/component-with-nested-childern-and-attributes.vue b/test/resources/components/component-with-nested-childern-and-attributes.vue
new file mode 100644
index 000000000..6f04db2f9
--- /dev/null
+++ b/test/resources/components/component-with-nested-childern-and-attributes.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/specs/mounting-options/stubs.spec.js b/test/specs/mounting-options/stubs.spec.js
index 905c369a7..8c8715462 100644
--- a/test/specs/mounting-options/stubs.spec.js
+++ b/test/specs/mounting-options/stubs.spec.js
@@ -2,6 +2,7 @@ import ComponentWithChild from '~resources/components/component-with-child.vue'
import ComponentWithNestedChildren from '~resources/components/component-with-nested-children.vue'
import Component from '~resources/components/component.vue'
import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
+import ComponentWithNestedChildrenAndAttributes from '~resources/components/component-with-nested-childern-and-attributes.vue'
import { createLocalVue, config } from '@vue/test-utils'
import { config as serverConfig } from '@vue/server-test-utils'
import Vue from 'vue'
@@ -18,6 +19,7 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
serverConfigSave = serverConfig.stubs
config.stubs = {}
serverConfig.stubs = {}
+ sandbox.stub(console, 'error').callThrough()
})
afterEach(() => {
@@ -32,21 +34,24 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
const ComponentWithoutRender = { template: '' }
const ExtendedComponent = { extends: ComponentWithRender }
const SubclassedComponent = Vue.extend({ template: '' })
+ const StringComponent = ''
mountingMethod(ComponentWithChild, {
stubs: {
ChildComponent: ComponentWithRender,
ChildComponent2: ComponentAsAClass,
ChildComponent3: ComponentWithoutRender,
ChildComponent4: ExtendedComponent,
- ChildComponent5: SubclassedComponent
+ ChildComponent5: SubclassedComponent,
+ ChildComponent6: StringComponent
}
})
})
it('replaces component with template string ', () => {
+ const Stub = { template: '' }
const wrapper = mountingMethod(ComponentWithChild, {
stubs: {
- ChildComponent: ''
+ ChildComponent: Stub
}
})
expect(wrapper.findAll('.stub').length).to.equal(1)
@@ -321,7 +326,7 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
const wrapper = mountingMethod(TestComponent, {
stubs: {
- 'span-component': ''
+ 'span-component': { template: '' }
},
localVue
})
@@ -342,7 +347,7 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
const wrapper = mountingMethod(TestComponent, {
stubs: {
- 'time-component': ''
+ 'time-component': { template: '' }
},
localVue
})
@@ -414,7 +419,7 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
expect(wrapper.html()).contains('No render function')
})
- it('throws an error when passed a circular reference', () => {
+ it('throws an error when passed a circular reference for string stubs', () => {
const names = ['child-component', 'ChildComponent', 'childComponent']
const validValues = [
'',
@@ -590,4 +595,52 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
expect(result.props().propA).to.equal('A')
delete Vue.options.components['child-component']
})
+
+ itRunIf(
+ vueVersion >= 2.2,
+ 'renders props in the element as attributes',
+ () => {
+ const ComponentStub = { template: '' }
+ const StringStub = ''
+ const BooleanStub = true
+
+ const wrapper = mountingMethod(ComponentWithNestedChildrenAndAttributes, {
+ stubs: {
+ SlotComponent: ComponentStub,
+ ChildComponent: StringStub,
+ OriginalComponent: BooleanStub
+ }
+ })
+
+ expect(wrapper.find('#component-stub').attributes()).to.eql({
+ id: 'component-stub',
+ prop1: 'foobar',
+ prop2: 'fizzbuzz'
+ })
+ expect(wrapper.find('#string-stub').attributes()).to.eql({
+ id: 'string-stub',
+ prop1: 'foobar',
+ prop2: 'fizzbuzz'
+ })
+ expect(wrapper.find('originalcomponent-stub').attributes()).to.eql({
+ prop1: 'foobar',
+ prop2: 'fizzbuzz'
+ })
+ }
+ )
+
+ it('warns when passing a string', () => {
+ const StringComponent = ''
+ mountingMethod(ComponentWithChild, {
+ stubs: {
+ ChildComponent6: StringComponent
+ }
+ })
+
+ expect(console.error).calledWith(
+ sandbox.match(
+ '[vue-test-utils]: String stubs are deprecated and will be removed in future versions'
+ )
+ )
+ })
})