Skip to content

Commit

Permalink
fix: stubs extended component correctly (#767)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyerburgh authored Jun 27, 2018
1 parent e83cda2 commit 24ab4c5
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 31 deletions.
88 changes: 58 additions & 30 deletions packages/shared/stub-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import Vue from 'vue'
import { compileToFunctions } from 'vue-template-compiler'
import { throwError } from './util'
import {
throwError,
camelize,
capitalize,
hyphenate
} from './util'
import {
componentNeedsCompiling,
templateContainsComponent
Expand All @@ -21,28 +26,37 @@ function isValidStub (stub: any) {
)
}

function resolveComponent (obj, component) {
return obj[component] ||
obj[hyphenate(component)] ||
obj[camelize(component)] ||
obj[capitalize(camelize(component))] ||
obj[capitalize(component)] ||
{}
}

function isRequiredComponent (name) {
return (
name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup'
)
}

function getCoreProperties (component: Component): Object {
function getCoreProperties (componentOptions: Component): Object {
return {
attrs: component.attrs,
name: component.name,
on: component.on,
key: component.key,
ref: component.ref,
props: component.props,
domProps: component.domProps,
class: component.class,
staticClass: component.staticClass,
staticStyle: component.staticStyle,
style: component.style,
normalizedStyle: component.normalizedStyle,
nativeOn: component.nativeOn,
functional: component.functional
attrs: componentOptions.attrs,
name: componentOptions.name,
on: componentOptions.on,
key: componentOptions.key,
ref: componentOptions.ref,
props: componentOptions.props,
domProps: componentOptions.domProps,
class: componentOptions.class,
staticClass: componentOptions.staticClass,
staticStyle: componentOptions.staticStyle,
style: componentOptions.style,
normalizedStyle: componentOptions.normalizedStyle,
nativeOn: componentOptions.nativeOn,
functional: componentOptions.functional
}
}
function createStubFromString (
Expand All @@ -62,24 +76,31 @@ function createStubFromString (
throwError('options.stub cannot contain a circular reference')
}

const componentOptions = typeof originalComponent === 'function'
? originalComponent.extendOptions
: originalComponent

return {
...getCoreProperties(originalComponent),
...getCoreProperties(componentOptions),
...compileToFunctions(templateString)
}
}

function createBlankStub (originalComponent: Component) {
const name = `${originalComponent.name}-stub`
function createBlankStub (originalComponent: Component, name: string) {
const componentOptions = typeof originalComponent === 'function'
? originalComponent.extendOptions
: originalComponent
const tagName = `${name}-stub`

// ignoreElements does not exist in Vue 2.0.x
if (Vue.config.ignoredElements) {
Vue.config.ignoredElements.push(name)
Vue.config.ignoredElements.push(tagName)
}

return {
...getCoreProperties(originalComponent),
...getCoreProperties(componentOptions),
render (h) {
return h(name)
return h(tagName)
}
}
}
Expand All @@ -101,7 +122,9 @@ export function createComponentStubs (
if (typeof stub !== 'string') {
throwError(`each item in an options.stubs array must be a ` + `string`)
}
components[stub] = createBlankStub({ name: stub })
const component = resolveComponent(originalComponents, stub)

components[stub] = createBlankStub(component, stub)
})
} else {
Object.keys(stubs).forEach(stub => {
Expand All @@ -114,7 +137,8 @@ export function createComponentStubs (
)
}
if (stubs[stub] === true) {
components[stub] = createBlankStub({ name: stub })
const component = resolveComponent(originalComponents, stub)
components[stub] = createBlankStub(component, stub)
return
}

Expand Down Expand Up @@ -162,12 +186,16 @@ export function createComponentStubs (

function stubComponents (components: Object, stubbedComponents: Object) {
Object.keys(components).forEach(component => {
const cmp = components[component]
const componentOptions = typeof cmp === 'function'
? cmp.extendOptions
: cmp
// Remove cached constructor
delete components[component]._Ctor
if (!components[component].name) {
components[component].name = component
delete componentOptions._Ctor
if (!componentOptions.name) {
componentOptions.name = component
}
stubbedComponents[component] = createBlankStub(components[component])
stubbedComponents[component] = createBlankStub(componentOptions, component)
})
}

Expand All @@ -178,7 +206,7 @@ export function createComponentStubsForAll (component: Component): Object {
stubComponents(component.components, stubbedComponents)
}

stubbedComponents[component.name] = createBlankStub(component)
stubbedComponents[component.name] = createBlankStub(component, component.name)

let extended = component.extends

Expand All @@ -204,7 +232,7 @@ export function createComponentStubsForGlobals (instance: Component): Object {
return
}

components[c] = createBlankStub(instance.options.components[c])
components[c] = createBlankStub(instance.options.components[c], c)
delete instance.options.components[c]._Ctor
delete components[c]._Ctor
})
Expand Down
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 @@ -106,7 +106,7 @@ export default function findVueComponents (
)
}
const nameSelector =
typeof selector === 'function' ? selector.options.name : selector.name
typeof selector === 'function' ? selector.extendOptions.name : selector.name
const components = root._isVue
? findAllVueComponentsFromVm(root)
: findAllVueComponentsFromVnode(root)
Expand Down
17 changes: 17 additions & 0 deletions test/specs/shallow-mount.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,23 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => {
).to.equal(3)
})

it('handles extended stubs', () => {
const ChildComponent = Vue.extend({
template: '<div />',
props: ['propA']
})
const TestComponent = {
template: '<child-component propA="hey" />',
components: { ChildComponent }
}
const wrapper = shallowMount(TestComponent, {
stubs: ['child-component']
})

expect(wrapper.find(ChildComponent).vm.propA)
.to.equal('hey')
})

it('throws an error when the component fails to mount', () => {
expect(() =>
shallowMount({
Expand Down
14 changes: 14 additions & 0 deletions test/specs/wrapper/find.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { compileToFunctions } from 'vue-template-compiler'
import { createLocalVue } from '~vue/test-utils'
import Vue from 'vue'
import ComponentWithChild from '~resources/components/component-with-child.vue'
import ComponentWithoutName from '~resources/components/component-without-name.vue'
import ComponentWithSlots from '~resources/components/component-with-slots.vue'
Expand Down Expand Up @@ -79,6 +80,19 @@ describeWithShallowAndMount('find', mountingMethod => {
expect(wrapper.find('#foo').vnode).to.be.an('object')
})

it('returns matching extended component', () => {
const ChildComponent = Vue.extend({
template: '<div />',
props: ['propA']
})
const TestComponent = {
template: '<child-component propA="hey" />',
components: { ChildComponent }
}
const wrapper = mountingMethod(TestComponent)
expect(wrapper.find(ChildComponent).vnode).to.be.an('object')
})

it('returns Wrapper of elements matching attribute selector passed', () => {
const compiled = compileToFunctions('<div><a href="/"></a></div>')
const wrapper = mountingMethod(compiled)
Expand Down

0 comments on commit 24ab4c5

Please sign in to comment.