Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: stub child components (#723)
Browse files Browse the repository at this point in the history
eddyerburgh authored and TheAlexLichter committed Jun 18, 2018

Verified

This commit was signed with the committer’s verified signature.
manasvinibs Manasvini B Suryanarayana
1 parent 7302e05 commit 31a3d0b
Showing 11 changed files with 118 additions and 71 deletions.
14 changes: 8 additions & 6 deletions packages/create-instance/add-slots.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ function startsWithTag (str) {
return str && str.trim()[0] === '<'
}

function createVNodesForSlot (
export function createVNodesForSlot (
h: Function,
slotValue: SlotValue,
name: string
@@ -30,12 +30,14 @@ export function createSlotVNodes (
return Object.keys(slots).reduce((acc, key) => {
const content = slots[key]
if (Array.isArray(content)) {
const nodes = content.reduce((accInner, slotDef) => {
return accInner.concat(createVNodesForSlot(h, slotDef, key))
}, [])
const nodes = content.map(slotDef => createVNodesForSlot(h, slotDef, key))
return acc.concat(nodes)
} else {
return acc.concat(createVNodesForSlot(h, content, key))
}

return acc.concat(createVNodesForSlot(h, content, key))
}, [])
}

export function createFunctionalSlotVNodes (h, slots = {}) {
return createSlotVNodes(h, slots)
}
35 changes: 2 additions & 33 deletions packages/create-instance/create-functional-component.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
// @flow

import { compileToFunctions } from 'vue-template-compiler'
import { throwError } from 'shared/util'
import { validateSlots } from './validate-slots'

function createFunctionalSlots (slots = {}, h) {
if (Array.isArray(slots.default)) {
return slots.default.map(h)
}

if (typeof slots.default === 'string') {
return [h(compileToFunctions(slots.default))]
}
const children = []
Object.keys(slots).forEach(slotType => {
if (Array.isArray(slots[slotType])) {
slots[slotType].forEach(slot => {
const component =
typeof slot === 'string' ? compileToFunctions(slot) : slot
const newSlot = h(component)
newSlot.data.slot = slotType
children.push(newSlot)
})
} else {
const component =
typeof slots[slotType] === 'string'
? compileToFunctions(slots[slotType])
: slots[slotType]
const slot = h(component)
slot.data.slot = slotType
children.push(slot)
}
})
return children
}
import { createFunctionalSlotVNodes } from './add-slots'

export default function createFunctionalComponent (
component: Component,
@@ -56,7 +25,7 @@ export default function createFunctionalComponent (
mountingOptions.context.children.map(
x => (typeof x === 'function' ? x(h) : x)
)) ||
createFunctionalSlots(mountingOptions.slots, h)
createFunctionalSlotVNodes(h, mountingOptions.slots)
)
},
name: component.name,
22 changes: 14 additions & 8 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
@@ -59,7 +59,14 @@ export default function createInstance (
...stubComponents
}
}

_Vue.mixin({
created () {
Object.assign(
this.$options.components,
stubComponents
)
}
})
Object.keys(component.components || {}).forEach(c => {
if (
component.components[c].extendOptions &&
@@ -79,14 +86,13 @@ export default function createInstance (
}
})

Object.keys(stubComponents).forEach(c => {
_Vue.component(c, stubComponents[c])
})
if (component.options) {
component.options._base = _Vue
}

const Constructor =
vueVersion < 2.3 && typeof component === 'function'
? component.extend(instanceOptions)
: _Vue.extend(component).extend(instanceOptions)
const Constructor = vueVersion < 2.3 && typeof component === 'function'
? component.extend(instanceOptions)
: _Vue.extend(component).extend(instanceOptions)

Object.keys(instanceOptions.components || {}).forEach(key => {
Constructor.component(key, instanceOptions.components[key])
8 changes: 6 additions & 2 deletions packages/server-test-utils/src/renderToString.js
Original file line number Diff line number Diff line change
@@ -28,8 +28,12 @@ export default function renderToString (
if (options.attachToDocument) {
throwError(`you cannot use attachToDocument with ` + `renderToString`)
}
const vueClass = options.localVue || testUtils.createLocalVue()
const vm = createInstance(component, mergeOptions(options, config), vueClass)
const vueConstructor = testUtils.createLocalVue(options.localVue)
const vm = createInstance(
component,
mergeOptions(options, config),
vueConstructor
)
let renderedString = ''

// $FlowIgnore
8 changes: 4 additions & 4 deletions packages/test-utils/src/create-local-vue.js
Original file line number Diff line number Diff line change
@@ -4,13 +4,13 @@ import Vue from 'vue'
import cloneDeep from 'lodash/cloneDeep'
import errorHandler from './error-handler'

function createLocalVue (): Component {
const instance = Vue.extend()
function createLocalVue (_Vue: Component = Vue): Component {
const instance = _Vue.extend()

// clone global APIs
Object.keys(Vue).forEach(key => {
Object.keys(_Vue).forEach(key => {
if (!instance.hasOwnProperty(key)) {
const original = Vue[key]
const original = _Vue[key]
instance[key] =
typeof original === 'object' ? cloneDeep(original) : original
}
3 changes: 1 addition & 2 deletions packages/test-utils/src/mount.js
Original file line number Diff line number Diff line change
@@ -28,8 +28,7 @@ export default function mount (

// Remove cached constructor
delete component._Ctor

const vueConstructor = options.localVue || createLocalVue()
const vueConstructor = createLocalVue(options.localVue)

const elm = options.attachToDocument ? createElement() : undefined

11 changes: 3 additions & 8 deletions test/specs/mount.spec.js
Original file line number Diff line number Diff line change
@@ -58,20 +58,15 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
}
})

it('returns new VueWrapper with mounted Vue instance initialized with Vue.extend with props, if passed as propsData', () => {
const prop1 = { test: 'TEST' }
it('handles propsData for extended components', () => {
const prop1 = 'test'
const TestComponent = Vue.extend(ComponentWithProps)
const wrapper = mount(TestComponent, {
propsData: {
prop1
}
})
expect(wrapper.vm).to.be.an('object')
if (wrapper.vm.$props) {
expect(wrapper.vm.$props.prop1).to.equal(prop1)
} else {
expect(wrapper.vm.$options.propsData.prop1).to.equal(prop1)
}
expect(wrapper.text()).to.contain(prop1)
})

it('handles uncompiled extended Vue component', () => {
8 changes: 8 additions & 0 deletions test/specs/mounting-options/localVue.spec.js
Original file line number Diff line number Diff line change
@@ -69,4 +69,12 @@ describeWithMountingMethods('options.localVue', mountingMethod => {
expect(HTML).to.contain('2')
}
})

it('does not add created mixin to localVue', () => {
const localVue = createLocalVue()
mountingMethod({ render: () => {} }, {
localVue
})
expect(localVue.options.created).to.equal(undefined)
})
})
19 changes: 19 additions & 0 deletions test/specs/mounting-options/slots.spec.js
Original file line number Diff line number Diff line change
@@ -235,6 +235,25 @@ describeWithMountingMethods('options.slots', mountingMethod => {
}
})

it('mounts functional component with text slot', () => {
const TestComponent = {
name: 'component-with-slots',
functional: true,
render: (h, ctx) => h('div', ctx.data, [ctx.slots().default, ctx.slots().header])
}
const wrapper = mountingMethod(TestComponent, {
slots: {
default: 'hello,',
header: 'world'
}
})
if (mountingMethod.name === 'renderToString') {
expect(wrapper).contains('hello,world')
} else {
expect(wrapper.text()).to.contain('hello,world')
}
})

it('mounts component with named slot if passed component in slot object', () => {
const wrapper = mountingMethod(ComponentWithSlots, {
slots: {
46 changes: 46 additions & 0 deletions test/specs/mounting-options/stubs.spec.js
Original file line number Diff line number Diff line change
@@ -128,6 +128,52 @@ describeWithMountingMethods('options.stub', mountingMethod => {
expect(HTML).to.contain('<registered-component-stub>')
})

itDoNotRunIf(
mountingMethod.name === 'shallowMount',
'stubs nested components', () => {
const GrandchildComponent = {
template: '<span />'
}
const ChildComponent = {
template: '<grandchild-component />',
components: { GrandchildComponent }
}
const TestComponent = {
template: '<child-component />',
components: { ChildComponent }
}
const wrapper = mountingMethod(TestComponent, {
stubs: ['grandchild-component']
})
const HTML = mountingMethod.name === 'renderToString'
? wrapper
: wrapper.html()
expect(HTML).not.to.contain('<span>')
})

itDoNotRunIf(
mountingMethod.name === 'shallowMount',
'stubs nested components on extended components', () => {
const GrandchildComponent = {
template: '<span />'
}
const ChildComponent = {
template: '<grandchild-component />',
components: { GrandchildComponent }
}
const TestComponent = {
template: '<div><child-component /></div>',
components: { ChildComponent }
}
const wrapper = mountingMethod(Vue.extend(TestComponent), {
stubs: ['grandchild-component']
})
const HTML = mountingMethod.name === 'renderToString'
? wrapper
: wrapper.html()
expect(HTML).not.to.contain('<span>')
})

it('stubs components with dummy when passed a boolean', () => {
const ComponentWithGlobalComponent = {
render: h => h('div', [h('registered-component')])
15 changes: 7 additions & 8 deletions test/specs/shallow-mount.spec.js
Original file line number Diff line number Diff line change
@@ -171,14 +171,13 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => {
expect(wrapper.find('p').exists()).to.equal(false)
})

it('stubs Vue class component children', () => {
if (vueVersion < 2.3) {
return
}
const wrapper = shallowMount(ComponentAsAClassWithChild)
expect(wrapper.find(Component).exists()).to.equal(true)
expect(wrapper.findAll('div').length).to.equal(1)
})
itDoNotRunIf(
vueVersion < 2.3,
'stubs Vue class component children', () => {
const wrapper = shallowMount(ComponentAsAClassWithChild)
expect(wrapper.find(Component).exists()).to.equal(true)
expect(wrapper.findAll('div').length).to.equal(1)
})

it('works correctly with find, contains, findAll, and is on unnamed components', () => {
const TestComponent = {

0 comments on commit 31a3d0b

Please sign in to comment.