diff --git a/packages/create-instance/create-component-stubs.js b/packages/create-instance/create-component-stubs.js index 5b6660761..1fd9f9df2 100644 --- a/packages/create-instance/create-component-stubs.js +++ b/packages/create-instance/create-component-stubs.js @@ -11,6 +11,8 @@ import { } from '../shared/validators' import { compileTemplate, compileFromString } from '../shared/compile-template' +const FUNCTION_PLACEHOLDER = '[Function]' + function isVueComponentStub(comp): boolean { return (comp && comp.template) || isVueComponent(comp) } @@ -98,7 +100,7 @@ export function createStubFromComponent( { attrs: componentOptions.functional ? { - ...context.props, + ...shapeStubProps(context.props), ...context.data.attrs, class: createClassString( context.data.staticClass, @@ -106,7 +108,7 @@ export function createStubFromComponent( ) } : { - ...this.$props + ...shapeStubProps(this.$props) } }, context ? context.children : this.$options._renderChildren @@ -139,6 +141,27 @@ function validateStub(stub) { } } +function shapeStubProps(props) { + const shapedProps: Object = {} + for (const propName in props) { + if (typeof props[propName] === 'function') { + shapedProps[propName] = FUNCTION_PLACEHOLDER + continue + } + + if (Array.isArray(props[propName])) { + shapedProps[propName] = props[propName].map(value => { + return typeof value === 'function' ? FUNCTION_PLACEHOLDER : value + }) + continue + } + + shapedProps[propName] = props[propName] + } + + return shapedProps +} + export function createStubsFromStubsObject( originalComponents: Object = {}, stubs: Object, diff --git a/test/specs/mounting-options/stubs.spec.js b/test/specs/mounting-options/stubs.spec.js index 7f398f2db..81c93fd0e 100644 --- a/test/specs/mounting-options/stubs.spec.js +++ b/test/specs/mounting-options/stubs.spec.js @@ -546,4 +546,69 @@ describeWithShallowAndMount('options.stub', mountingMethod => { expect(wrapper.find(ToStub).exists()).to.be.false expect(wrapper.find(Stub).exists()).to.be.true }) + + it('should render functions in props as a deterministic string', () => { + const ChildComponent = { + name: 'child-component', + props: { + foo: { + type: Function, + required: true + }, + bar: { + type: Array, + required: true + }, + qux: { + type: String, + required: true + } + }, + template: '
' + } + + const FunctionalChildComponent = { + ...ChildComponent, + name: 'functional-child-component', + functional: true + } + + const ParentComponent = { + components: { + ChildComponent, + FunctionalChildComponent + }, + name: 'parent-component', + template: ` +
+ + +
+ `, + data() { + return { + foo: () => 42, + bar: [1, 'string', () => true], + qux: 'qux' + } + } + } + + const wrapper = mountingMethod(ParentComponent, { + stubs: ['child-component', 'functional-child-component'] + }) + + expect(wrapper.html()).to.equal( + `
\n` + + ` \n` + + ` \n` + + `
` + ) + }) })