Skip to content

refactor: use validator functions #1107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions packages/create-instance/create-component-stubs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
import {
componentNeedsCompiling,
templateContainsComponent,
isVueComponent
isVueComponent,
isDynamicComponent,
isConstructor
} from '../shared/validators'
import {
compileTemplate,
Expand Down Expand Up @@ -65,11 +67,11 @@ function createClassString (staticClass, dynamicClass) {
}

function resolveOptions (component, _Vue) {
if (typeof component === 'function' && !component.cid) {
if (isDynamicComponent(component)) {
return {}
}

return typeof component === 'function'
return isConstructor(component)
? component.options
: _Vue.extend(component).options
}
Expand Down Expand Up @@ -112,19 +114,16 @@ export function createStubFromComponent (
}
}

export function createStubFromString (
function createStubFromString (
templateString: string,
originalComponent: Component = {},
name: string
name: string,
_Vue: Component
): Component {
if (templateContainsComponent(templateString, name)) {
throwError('options.stub cannot contain a circular reference')
}

const componentOptions =
typeof originalComponent === 'function' && originalComponent.cid
? originalComponent.extendOptions
: originalComponent
const componentOptions = resolveOptions(originalComponent, _Vue)

return {
...getCoreProperties(componentOptions),
Expand Down Expand Up @@ -167,7 +166,8 @@ export function createStubsFromStubsObject (
acc[stubName] = createStubFromString(
stub,
component,
stubName
stubName,
_Vue
)
return acc
}
Expand Down
43 changes: 19 additions & 24 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { validateSlots } from './validate-slots'
import createScopedSlots from './create-scoped-slots'
import { createStubsFromStubsObject } from './create-component-stubs'
import { patchCreateElement } from './patch-create-element'
import { isConstructor } from 'shared/validators'

function vueExtendUnsupportedOption (option: string) {
return `options.${option} is not supported for ` +
Expand All @@ -39,13 +40,8 @@ export default function createInstance (
options: Options,
_Vue: Component
): Component {
// make sure all extends are based on this instance
_Vue.options._base = _Vue

if (
VUE_VERSION < 2.3 &&
typeof component === 'function' &&
component.options
VUE_VERSION < 2.3 && isConstructor(component)
) {
UNSUPPORTED_VERSION_OPTIONS.forEach((option) => {
if (options[option]) {
Expand All @@ -54,11 +50,15 @@ export default function createInstance (
})
}

let componentOptions = isConstructor(component)
? component.options
: component

// instance options are options that are passed to the
// root instance when it's instantiated
const instanceOptions = extractInstanceOptions(options)
const stubComponentsObject = createStubsFromStubsObject(
component.components,
componentOptions.components,
// $FlowIgnore
options.stubs,
_Vue
Expand All @@ -69,31 +69,27 @@ export default function createInstance (
addStubs(_Vue, stubComponentsObject)
patchCreateElement(_Vue, stubComponentsObject, options.shouldProxy)

if (
(component.options && component.options.functional) ||
component.functional
) {
component = createFunctionalComponent(component, options, _Vue)
if (componentOptions.functional) {
componentOptions = createFunctionalComponent(
componentOptions,
options,
_Vue
)
} else if (options.context) {
throwError(
`mount.context can only be used when mounting a ` +
`functional component`
)
}

if (componentNeedsCompiling(component)) {
compileTemplate(component)
if (componentNeedsCompiling(componentOptions)) {
compileTemplate(componentOptions)
}

if (component.options) {
component.options._base = _Vue
}
// make sure all extends are based on this instance
componentOptions._base = _Vue

// extend component from _Vue to add properties and mixins
// extend does not work correctly for sub class components in Vue < 2.2
const Constructor = typeof component === 'function'
? _Vue.extend(component.options).extend(instanceOptions)
: _Vue.extend(component).extend(instanceOptions)
const Constructor = _Vue.extend(componentOptions).extend(instanceOptions)

// used to identify extended component using constructor
Constructor.options.$_vueTestUtils_original = component
Expand Down Expand Up @@ -122,8 +118,7 @@ export default function createInstance (

if (options.parentComponent && !isPlainObject(options.parentComponent)) {
throwError(
`options.parentComponent should be a valid Vue component ` +
`options object`
`options.parentComponent should be a valid Vue component options object`
)
}

Expand Down
27 changes: 9 additions & 18 deletions packages/create-instance/patch-create-element.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { createStubFromComponent } from './create-component-stubs'
import { resolveComponent } from 'shared/util'
import { isReservedTag } from 'shared/validators'
import {
isReservedTag,
isConstructor,
isDynamicComponent,
isComponentOptions
} from 'shared/validators'
import {
BEFORE_RENDER_LIFECYCLE_HOOK,
CREATE_ELEMENT_ALIAS
} from 'shared/consts'

const isWhitelisted = (el, whitelist) => resolveComponent(el, whitelist)
const isAlreadyStubbed = (el, stubs) => stubs.has(el)
const isDynamicComponent = cmp => typeof cmp === 'function' && !cmp.cid

function shouldExtend (component, _Vue) {
return (
(typeof component === 'function' && !isDynamicComponent(component)) ||
isConstructor(component) ||
(component && component.extends)
)
}
Expand All @@ -21,6 +25,7 @@ function extend (component, _Vue) {
const componentOptions = component.options ? component.options : component
const stub = _Vue.extend(componentOptions)
stub.options.$_vueTestUtils_original = component
stub.options._base = _Vue
return stub
}

Expand All @@ -42,14 +47,6 @@ function shouldNotBeStubbed (el, whitelist, modifiedComponents) {
)
}

function isConstructor (el) {
return typeof el === 'function'
}

function isComponentOptions (el) {
return typeof el === 'object' && (el.template || el.render)
}

export function patchCreateElement (_Vue, stubs, stubAllComponents) {
// This mixin patches vm.$createElement so that we can stub all components
// before they are rendered in shallow mode. We also need to ensure that
Expand Down Expand Up @@ -87,7 +84,7 @@ export function patchCreateElement (_Vue, stubs, stubAllComponents) {
}

if (typeof el === 'string') {
let original = resolveComponent(el, originalComponents)
const original = resolveComponent(el, originalComponents)

if (!original) {
return originalCreateElement(el, ...args)
Expand All @@ -97,12 +94,6 @@ export function patchCreateElement (_Vue, stubs, stubAllComponents) {
return originalCreateElement(el, ...args)
}

if (
original.options &&
original.options.$_vueTestUtils_original
) {
original = original.options.$_vueTestUtils_original
}
const stub = createStubIfNeeded(stubAllComponents, original, _Vue, el)

if (stub) {
Expand Down
28 changes: 20 additions & 8 deletions packages/shared/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,24 @@ export function isDomSelector (selector: any): boolean {
}
}

export function isVueComponent (component: any): boolean {
if (typeof component === 'function' && component.options) {
export function isVueComponent (c: any): boolean {
if (isConstructor(c)) {
return true
}

if (component === null || typeof component !== 'object') {
if (c === null || typeof c !== 'object') {
return false
}

if (component.extends || component._Ctor) {
if (c.extends || c._Ctor) {
return true
}

if (typeof component.template === 'string') {
if (typeof c.template === 'string') {
return true
}

return typeof component.render === 'function'
return typeof c.render === 'function'
}

export function componentNeedsCompiling (component: Component): boolean {
Expand Down Expand Up @@ -76,6 +76,18 @@ export function isNameSelector (nameOptionsObject: any): boolean {
return !!nameOptionsObject.name
}

export function isConstructor (c: any) {
return typeof c === 'function' && c.cid
}

export function isDynamicComponent (c: any) {
return typeof c === 'function' && !c.cid
}

export function isComponentOptions (c: any) {
return typeof c === 'object' && (c.template || c.render)
}

export function templateContainsComponent (
template: string,
name: string
Expand All @@ -86,8 +98,8 @@ export function templateContainsComponent (
})
}

export function isPlainObject (obj: any): boolean {
return Object.prototype.toString.call(obj) === '[object Object]'
export function isPlainObject (c: any): boolean {
return Object.prototype.toString.call(c) === '[object Object]'
}

export function isRequiredComponent (name: string): boolean {
Expand Down
7 changes: 4 additions & 3 deletions packages/test-utils/src/matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
COMPONENT_SELECTOR,
FUNCTIONAL_OPTIONS
} from 'shared/consts'
import { isConstructor } from 'shared/validators'

export function vmMatchesName (vm, name) {
return !!name && (
Expand All @@ -19,7 +20,7 @@ function vmCtorMatches (vm, component) {
return true
}

const Ctor = typeof component === 'function'
const Ctor = isConstructor(component)
? component.options._Ctor
: component._Ctor

Expand All @@ -46,7 +47,7 @@ export function matches (node, selector) {
return element && element.matches && element.matches(selector.value)
}

const isFunctionalSelector = typeof selector.value === 'function'
const isFunctionalSelector = isConstructor(selector.value)
? selector.value.options.functional
: selector.value.functional

Expand All @@ -66,7 +67,7 @@ export function matches (node, selector) {

// Fallback to name selector for COMPONENT_SELECTOR for Vue < 2.1
const nameSelector =
typeof selector.value === 'function'
isConstructor(selector.value)
? selector.value.extendOptions.name
: selector.value.name
return vmMatchesName(componentInstance, nameSelector)
Expand Down
26 changes: 26 additions & 0 deletions test/specs/mount.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,32 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
expect(wrapper.findAll(ChildComponent).length).to.equal(1)
})

it('handles nested components with extends', () => {
const GrandChildComponent = {
template: '<div />',
created () {
this.$route.params
}
}
const ChildComponent = Vue.extend({
template: '<grand-child-component />',
components: {
GrandChildComponent
}
})
const TestComponent = {
template: '<child-component />',
components: {
ChildComponent
}
}
const localVue = createLocalVue()
localVue.prototype.$route = {}
mount(TestComponent, {
localVue
})
})

it('throws if component throws during update', () => {
const TestComponent = {
template: '<div :p="a" />',
Expand Down
2 changes: 1 addition & 1 deletion test/specs/mounting-options/stubs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ describeWithMountingMethods('options.stub', mountingMethod => {
components: { GrandChildComponent }
}
const TestComponent = {
template: '<div><child-component /></div>',
template: '<child-component />',
components: { ChildComponent }
}
const wrapper = mountingMethod(Vue.extend(TestComponent), {
Expand Down