Skip to content

Commit 97fdf18

Browse files
authored
feat(stubs): render function props deterministically (#1834)
1 parent a1b4d05 commit 97fdf18

File tree

2 files changed

+90
-2
lines changed

2 files changed

+90
-2
lines changed

packages/create-instance/create-component-stubs.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
} from '../shared/validators'
1919
import { compileTemplate } from '../shared/compile-template'
2020

21+
const FUNCTION_PLACEHOLDER = '[Function]'
22+
2123
function isVueComponentStub(comp): boolean {
2224
return (comp && comp.template) || isVueComponent(comp)
2325
}
@@ -116,13 +118,13 @@ export function createStubFromComponent(
116118
? {
117119
...context.data,
118120
attrs: {
119-
...context.props,
121+
...shapeStubProps(context.props),
120122
...context.data.attrs
121123
}
122124
}
123125
: {
124126
attrs: {
125-
...this.$props
127+
...shapeStubProps(this.$props)
126128
}
127129
},
128130
context
@@ -136,6 +138,27 @@ export function createStubFromComponent(
136138
}
137139
}
138140

141+
function shapeStubProps(props) {
142+
const shapedProps: Object = {}
143+
for (const propName in props) {
144+
if (typeof props[propName] === 'function') {
145+
shapedProps[propName] = FUNCTION_PLACEHOLDER
146+
continue
147+
}
148+
149+
if (Array.isArray(props[propName])) {
150+
shapedProps[propName] = props[propName].map(value => {
151+
return typeof value === 'function' ? FUNCTION_PLACEHOLDER : value
152+
})
153+
continue
154+
}
155+
156+
shapedProps[propName] = props[propName]
157+
}
158+
159+
return shapedProps
160+
}
161+
139162
// DEPRECATED: converts string stub to template stub.
140163
function createStubFromString(templateString: string, name: string): Component {
141164
warnDeprecated('Using a string for stubs')

test/specs/mounting-options/stubs.spec.js

+65
Original file line numberDiff line numberDiff line change
@@ -639,4 +639,69 @@ describeWithShallowAndMount('options.stub', mountingMethod => {
639639
)
640640
config.showDeprecationWarnings = false
641641
})
642+
643+
it('should render functions in props as a deterministic string', () => {
644+
const ChildComponent = {
645+
name: 'child-component',
646+
props: {
647+
foo: {
648+
type: Function,
649+
required: true
650+
},
651+
bar: {
652+
type: Array,
653+
required: true
654+
},
655+
qux: {
656+
type: String,
657+
required: true
658+
}
659+
},
660+
template: '<div />'
661+
}
662+
663+
const FunctionalChildComponent = {
664+
...ChildComponent,
665+
name: 'functional-child-component',
666+
functional: true
667+
}
668+
669+
const ParentComponent = {
670+
components: {
671+
ChildComponent,
672+
FunctionalChildComponent
673+
},
674+
name: 'parent-component',
675+
template: `
676+
<div>
677+
<child-component
678+
:foo="foo"
679+
:bar="bar"
680+
:qux="qux" />
681+
<functional-child-component
682+
:foo="foo"
683+
:bar="bar"
684+
:qux="qux" />
685+
</div>
686+
`,
687+
data() {
688+
return {
689+
foo: () => 42,
690+
bar: [1, 'string', () => true],
691+
qux: 'qux'
692+
}
693+
}
694+
}
695+
696+
const wrapper = mountingMethod(ParentComponent, {
697+
stubs: ['child-component', 'functional-child-component']
698+
})
699+
700+
expect(wrapper.html()).toEqual(
701+
`<div>\n` +
702+
` <child-component-stub foo="[Function]" bar="1,string,[Function]" qux="qux"></child-component-stub>\n` +
703+
` <functional-child-component-stub foo="[Function]" bar="1,string,[Function]" qux="qux"></functional-child-component-stub>\n` +
704+
`</div>`
705+
)
706+
})
642707
})

0 commit comments

Comments
 (0)