From 99a3f2a84afa1fb16d0a4ee681c1d7fe2f5ca157 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 12:55:09 +0000 Subject: [PATCH 01/17] refactor: handle shallow mounting stubs in createElement --- packages/create-instance/add-stubs.js | 103 +++++++++++++++--- .../create-functional-component.js | 1 + packages/create-instance/create-instance.js | 5 + packages/shared/create-component-stubs.js | 59 +--------- packages/shared/util.js | 34 +++++- packages/shared/validators.js | 39 +++++++ packages/test-utils/src/shallow-mount.js | 28 +---- test/resources/utils.js | 4 +- test/specs/shallow-mount.spec.js | 18 ++- test/specs/wrapper-array/setData.spec.js | 7 +- test/specs/wrapper-array/setProps.spec.js | 10 +- test/specs/wrapper/findAll.spec.js | 8 +- test/specs/wrapper/trigger.spec.js | 4 +- 13 files changed, 185 insertions(+), 135 deletions(-) diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index 591db4b89..2e1794439 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -2,29 +2,98 @@ import { createStubsFromStubsObject, createStubFromComponent } from 'shared/create-component-stubs' +import { resolveComponent } from 'shared/util' +import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' +function stubComponent ( + el, + options, + componentStubs, + originalComponents, + whitelist +) { + if (typeof el === 'string' && !isReservedTag(el)) { + if (componentStubs.has(el) || resolveComponent(el, whitelist)) { + return el + } + + let original = resolveComponent(el, originalComponents) + + if ( + original && + original.options && + original.options.$_vueTestUtils_original + ) { + original = original.options.$_vueTestUtils_original + } + + if ( + typeof original === 'function' && + !original.options && + !original.functional + ) { + return el + } + + const stub = createStubFromComponent(original || {}, el) + + componentStubs.add(el) + + options.components = { + ...options.components, + [el]: stub + } + + return el + } + + if (typeof el === 'function' && !el.options) { + return el + } + + if (typeof el === 'function' || typeof el === 'object') { + const stub = createStubFromComponent(el, el.name || 'anonymous') + return stub + } + return el +} + export function addStubs (component, stubs, _Vue, shouldProxy) { - const stubComponents = createStubsFromStubsObject( - component.components, - stubs - ) + const stubComponents = createStubsFromStubsObject(component.components, stubs) function addStubComponentsMixin () { - Object.assign( - this.$options.components, - stubComponents - ) - if (typeof Proxy !== 'undefined' && shouldProxy) { - this.$options.components = new Proxy(this.$options.components, { - set (target, prop, value) { - if (!target[prop]) { - target[prop] = createStubFromComponent(value, prop) - } - return true - } - }) + Object.assign(this.$options.components, stubComponents) + } + + function patchRender () { + const vm = this + if (vm.$options.doNotStubRender || vm._isFunctionalContainer) { + return } + + const componentStubs = new Set() + const originalCreateElement = vm.$createElement + const originalComponents = vm.$options.components + + const createElement = (el, ...args) => { + const comp = stubComponent( + el, + vm.$options, + componentStubs, + originalComponents, + stubComponents + ) + + return originalCreateElement(comp, ...args) + } + + vm._c = createElement + vm.$createElement = createElement + } + + if (shouldProxy) { + addHook(_Vue.options, 'beforeCreate', patchRender) } addHook(_Vue.options, 'beforeMount', addStubComponentsMixin) diff --git a/packages/create-instance/create-functional-component.js b/packages/create-instance/create-functional-component.js index 61d07aaf9..c05ed3b6f 100644 --- a/packages/create-instance/create-functional-component.js +++ b/packages/create-instance/create-functional-component.js @@ -21,6 +21,7 @@ export default function createFunctionalComponent ( data.scopedSlots = createScopedSlots(mountingOptions.scopedSlots) return { + doNotStubRender: true, render (h: Function) { return h( component, diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 7b2a8f761..67bc4ed7c 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -112,6 +112,7 @@ export default function createInstance ( // used to identify extended component using constructor Constructor.options.$_vueTestUtils_original = component + if (options.slots) { compileTemplateForSlots(options.slots) // validate slots outside of the createSlots function so @@ -143,6 +144,9 @@ export default function createInstance ( const parentComponentOptions = options.parentComponent || {} parentComponentOptions.provide = options.provide + parentComponentOptions.doNotStubRender = true + parentComponentOptions.name = 'parent' + parentComponentOptions.render = function (h) { const slots = options.slots ? createSlotVNodes(this, options.slots) @@ -151,6 +155,7 @@ export default function createInstance ( Constructor, { ref: 'vm', + name: 'root', on: options.listeners, attrs: { ...options.attrs, diff --git a/packages/shared/create-component-stubs.js b/packages/shared/create-component-stubs.js index 3396d2b22..6d720d65b 100644 --- a/packages/shared/create-component-stubs.js +++ b/packages/shared/create-component-stubs.js @@ -81,6 +81,7 @@ export function createStubFromComponent ( return { ...getCoreProperties(componentOptions), $_vueTestUtils_original: originalComponent, + doNotStubRender: true, render (h, context) { return h( tagName, @@ -117,6 +118,7 @@ export function createStubFromString ( return { ...getCoreProperties(componentOptions), + doNotStubRender: true, ...compileFromString(templateString) } } @@ -172,60 +174,3 @@ export function createStubsFromStubsObject ( return acc }, {}) } - -function stubComponents ( - components: Components, - stubbedComponents: Components -): void { - for (const component in components) { - const cmp = components[component] - const componentOptions = typeof cmp === 'function' - ? cmp.extendOptions - : cmp - - if (!componentOptions) { - stubbedComponents[component] = createStubFromComponent( - {}, - component - ) - return - } - - stubbedComponents[component] = createStubFromComponent( - cmp, - component - ) - } -} - -export function createStubsForComponent ( - component: Component -): Components { - const stubbedComponents = {} - - if (component.options) { - stubComponents(component.options.components, stubbedComponents) - } - - if (component.components) { - stubComponents(component.components, stubbedComponents) - } - - let extended = component.extends - while (extended) { - if (extended.components) { - stubComponents(extended.components, stubbedComponents) - } - extended = extended.extends - } - - let extendOptions = component.extendOptions - while (extendOptions) { - if (extendOptions && extendOptions.components) { - stubComponents(extendOptions.components, stubbedComponents) - } - extendOptions = extendOptions.extendOptions - } - - return stubbedComponents -} diff --git a/packages/shared/util.js b/packages/shared/util.js index 1b78c8506..334761359 100644 --- a/packages/shared/util.js +++ b/packages/shared/util.js @@ -11,9 +11,8 @@ export function warn (msg: string): void { const camelizeRE = /-(\w)/g export const camelize = (str: string): string => { - const camelizedStr = str.replace( - camelizeRE, - (_, c) => (c ? c.toUpperCase() : '') + const camelizedStr = str.replace(camelizeRE, (_, c) => + c ? c.toUpperCase() : '' ) return camelizedStr.charAt(0).toLowerCase() + camelizedStr.slice(1) } @@ -34,3 +33,32 @@ export const hyphenate = (str: string): string => export const vueVersion = Number( `${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}` ) + +function hasOwnProperty (obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop) +} + +export function resolveComponent (id, components) { + /* istanbul ignore if */ + if (typeof id !== 'string') { + return + } + // var components = options['components']; + + // check local registration variations first + if (hasOwnProperty(components, id)) { + return components[id] + } + var camelizedId = camelize(id) + if (hasOwnProperty(components, camelizedId)) { + return components[camelizedId] + } + var PascalCaseId = capitalize(camelizedId) + if (hasOwnProperty(components, PascalCaseId)) { + return components[PascalCaseId] + } + // fallback to prototype chain + var res = + components[id] || components[camelizedId] || components[PascalCaseId] + return res +} diff --git a/packages/shared/validators.js b/packages/shared/validators.js index 8259e753f..be0434bef 100644 --- a/packages/shared/validators.js +++ b/packages/shared/validators.js @@ -95,3 +95,42 @@ export function isRequiredComponent (name: string): boolean { name === 'KeepAlive' || name === 'Transition' || name === 'TransitionGroup' ) } + +function makeMap ( + str, + expectsLowerCase +) { + var map = Object.create(null) + var list = str.split(',') + for (var i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? function (val) { return map[val.toLowerCase()] } + : function (val) { return map[val] } +} + +export const isHTMLTag = makeMap( + 'html,body,base,head,link,meta,style,title,' + + 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' + + 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' + + 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' + + 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,' + + 'embed,object,param,source,canvas,script,noscript,del,ins,' + + 'caption,col,colgroup,table,thead,tbody,td,th,tr,video,' + + 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' + + 'output,progress,select,textarea,' + + 'details,dialog,menu,menuitem,summary,' + + 'content,element,shadow,template,blockquote,iframe,tfoot' +) + +// this map is intentionally selective, only covering SVG elements that may +// contain child elements. +export const isSVG = makeMap( + 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' + + 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' + + 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view', + true +) + +export const isReservedTag = (tag) => isHTMLTag(tag) || isSVG(tag) diff --git a/packages/test-utils/src/shallow-mount.js b/packages/test-utils/src/shallow-mount.js index dd77dbd2b..2376c4495 100644 --- a/packages/test-utils/src/shallow-mount.js +++ b/packages/test-utils/src/shallow-mount.js @@ -1,40 +1,14 @@ // @flow -import './warn-if-no-window' -import Vue from 'vue' import mount from './mount' import type VueWrapper from './vue-wrapper' -import { - createStubsForComponent, - createStubFromComponent -} from '../../shared/create-component-stubs' -import { normalizeStubs } from '../../shared/normalize' export default function shallowMount ( component: Component, options: Options = {} ): VueWrapper { - const _Vue = options.localVue || Vue - - options.stubs = normalizeStubs(options.stubs) - - // Vue registers a recursive component on the original options - // This stub will override the component added by Vue - // $FlowIgnore - if (options.stubs && !options.stubs[component.name]) { - // $FlowIgnore - options.stubs[component.name] = createStubFromComponent( - component, - component.name - ) - } - return mount(component, { ...options, - shouldProxy: true, - components: { - ...createStubsForComponent(_Vue), - ...createStubsForComponent(component) - } + shouldProxy: true }) } diff --git a/test/resources/utils.js b/test/resources/utils.js index eabb68c92..d926ddbdf 100644 --- a/test/resources/utils.js +++ b/test/resources/utils.js @@ -29,9 +29,9 @@ export const functionalSFCsSupported = vueVersion > 2.4 export const scopedSlotsSupported = vueVersion > 2 const shallowAndMount = - process.env.TEST_ENV === 'node' ? [] : [mount, shallowMount] + process.env.TEST_ENV === 'node' ? [] : [shallowMount, mount] const shallowMountAndRender = - process.env.TEST_ENV === 'node' ? [renderToString] : [mount, shallowMount] + process.env.TEST_ENV === 'node' ? [renderToString] : [shallowMount, mount] export function describeWithShallowAndMount (spec, cb) { if (shallowAndMount.length > 0) { diff --git a/test/specs/shallow-mount.spec.js b/test/specs/shallow-mount.spec.js index 9568ef30e..37d48889a 100644 --- a/test/specs/shallow-mount.spec.js +++ b/test/specs/shallow-mount.spec.js @@ -33,6 +33,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => { const wrapper = shallowMount(ComponentWithNestedChildren) expect(wrapper.isVueInstance()).to.equal(true) expect(wrapper.findAll(Component).length).to.equal(0) + expect(wrapper.findAll(ComponentWithChild).length).to.equal(1) }) @@ -47,6 +48,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => { const wrapper = shallowMount(ComponentWithNestedChildren) expect(wrapper.findAll(Component).length).to.equal(0) const mountedWrapper = mount(ComponentWithNestedChildren) + expect(mountedWrapper.findAll(Component).length).to.equal(1) }) @@ -359,17 +361,13 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => { it('works correctly with find on recursive components', () => { // this is for a bug that I've been unable to replicate. // Sometimes components mutate their components, in this line— - RecursiveComponent.components = { - RecursiveComponent: { render: h => h('div') } - } + const wrapper = shallowMount(RecursiveComponent, { + propsData: { + items: ['', ''] + } + }) - expect( - shallowMount(RecursiveComponent, { - propsData: { - items: ['', ''] - } - }).findAll(RecursiveComponent).length - ).to.equal(3) + expect(wrapper.findAll(RecursiveComponent).length).to.equal(3) }) it('handles extended stubs', () => { diff --git a/test/specs/wrapper-array/setData.spec.js b/test/specs/wrapper-array/setData.spec.js index b0635da19..2ebf8ef6e 100644 --- a/test/specs/wrapper-array/setData.spec.js +++ b/test/specs/wrapper-array/setData.spec.js @@ -3,11 +3,8 @@ import ComponentWithVIf from '~resources/components/component-with-v-if.vue' import { describeWithShallowAndMount } from '~resources/utils' describeWithShallowAndMount('setData', mountingMethod => { - it('sets component data and updates nested vm nodes when called on Vue instance', () => { - const TestComponent = { - render: h => h(ComponentWithVIf) - } - const wrapper = mountingMethod(TestComponent) + it('sets component data and updates nested vm nodes', () => { + const wrapper = mountingMethod(ComponentWithVIf) const componentArr = wrapper.findAll(ComponentWithVIf) expect(componentArr.at(0).findAll('.child.ready').length).to.equal(0) componentArr.setData({ ready: true }) diff --git a/test/specs/wrapper-array/setProps.spec.js b/test/specs/wrapper-array/setProps.spec.js index 0909d7a0e..96dbedcaf 100644 --- a/test/specs/wrapper-array/setProps.spec.js +++ b/test/specs/wrapper-array/setProps.spec.js @@ -7,22 +7,16 @@ describeWithShallowAndMount('setProps', mountingMethod => { const prop1 = 'prop 1' const prop2 = 'prop 2' const propsData = { prop1: 'a prop', prop2 } - const TestComponent = { - render: h => h(ComponentWithProps, { props: propsData }) - } - const wrapper = mountingMethod(TestComponent) + const wrapper = mountingMethod(ComponentWithProps, { propsData }) wrapper.findAll(ComponentWithProps).setProps({ prop1 }) expect(wrapper.find('.prop-1').element.textContent).to.equal(prop1) expect(wrapper.find('.prop-2').element.textContent).to.equal(prop2) }) it('sets component props, and updates DOM when propsData was not initially passed', () => { - const TestComponent = { - render: h => h(ComponentWithProps) - } const prop1 = 'prop 1' const prop2 = 'prop s' - const wrapper = mountingMethod(TestComponent) + const wrapper = mountingMethod(ComponentWithProps) wrapper.findAll(ComponentWithProps).setProps({ prop1, prop2 }) expect(wrapper.find('.prop-1').element.textContent).to.equal(prop1) expect(wrapper.find('.prop-2').element.textContent).to.equal(prop2) diff --git a/test/specs/wrapper/findAll.spec.js b/test/specs/wrapper/findAll.spec.js index 72e785a5f..738ade7be 100644 --- a/test/specs/wrapper/findAll.spec.js +++ b/test/specs/wrapper/findAll.spec.js @@ -37,7 +37,7 @@ describeWithShallowAndMount('findAll', mountingMethod => { itDoNotRunIf( isRunningPhantomJS, - 'returns an array of Wrapper of elements matching class selector passed if they are declared inside a slot', + 'returns nodes matching class selector inside a slot', () => { const wrapper = mountingMethod(ComponentWithSlots, { slots: { @@ -49,8 +49,8 @@ describeWithShallowAndMount('findAll', mountingMethod => { } ) - it('returns an array of Wrapper of elements matching class selector passed if they are declared inside a functional component', () => { - const Component = { + it('returns nodes matching selector in a functional component', () => { + const TestComponent = { functional: true, render (h) { return h('p', {}, [ @@ -65,7 +65,7 @@ describeWithShallowAndMount('findAll', mountingMethod => { name: 'common' } - const wrapper = mountingMethod(Component) + const wrapper = mountingMethod(TestComponent) expect(wrapper.findAll('p').length).to.equal(3) }) diff --git a/test/specs/wrapper/trigger.spec.js b/test/specs/wrapper/trigger.spec.js index 74fdc3c0d..9f6a4e477 100644 --- a/test/specs/wrapper/trigger.spec.js +++ b/test/specs/wrapper/trigger.spec.js @@ -126,7 +126,7 @@ describeWithShallowAndMount('trigger', mountingMethod => { }) itDoNotRunIf( - !scopedSlotsSupported, + !scopedSlotsSupported || mountingMethod.name === 'shallowMount', 'handles instances without update watchers', () => { const vm = new Vue() @@ -141,7 +141,7 @@ describeWithShallowAndMount('trigger', mountingMethod => { } } const wrapper = mountingMethod(TestComponent) - + console.log(wrapper.html()) wrapper.findAll('button').trigger('click') } ) From 77a11f65341f4ee949d84b314d23f432e30436ee Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 14:32:33 +0000 Subject: [PATCH 02/17] refactor: update resolveElement function --- packages/create-instance/add-stubs.js | 75 ++++++++++++++------------- test/specs/wrapper/trigger.spec.js | 1 - 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index 2e1794439..b7bd35fab 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -6,56 +6,57 @@ import { resolveComponent } from 'shared/util' import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' -function stubComponent ( +const shouldNotBeStubbed = (el, whitelist) => resolveComponent(el, whitelist) +const isAlreadyStubbed = (el, stubs) => stubs.has(el) + +const isDynamicComponent = cmp => { + return typeof cmp === 'function' && + !cmp.options && + !cmp.functional +} + +function resolveElement ( el, options, componentStubs, originalComponents, whitelist ) { - if (typeof el === 'string' && !isReservedTag(el)) { - if (componentStubs.has(el) || resolveComponent(el, whitelist)) { - return el - } - - let original = resolveComponent(el, originalComponents) - - if ( - original && - original.options && - original.options.$_vueTestUtils_original - ) { - original = original.options.$_vueTestUtils_original - } - - if ( - typeof original === 'function' && - !original.options && - !original.functional - ) { - return el - } - - const stub = createStubFromComponent(original || {}, el) + if ( + isReservedTag(el) || + shouldNotBeStubbed(el, whitelist) || + isAlreadyStubbed(el, componentStubs) + ) { + return el + } - componentStubs.add(el) + if (typeof el === 'function' || typeof el === 'object') { + return createStubFromComponent(el, el.name || 'anonymous') + } - options.components = { - ...options.components, - [el]: stub - } + let original = resolveComponent(el, originalComponents) - return el + if ( + original && + original.options && + original.options.$_vueTestUtils_original + ) { + original = original.options.$_vueTestUtils_original } - if (typeof el === 'function' && !el.options) { + if (isDynamicComponent(original)) { return el } - if (typeof el === 'function' || typeof el === 'object') { - const stub = createStubFromComponent(el, el.name || 'anonymous') - return stub + const stub = createStubFromComponent(original || {}, el) + + componentStubs.add(el) + + options.components = { + ...options.components, + [el]: stub } + return el } @@ -77,7 +78,7 @@ export function addStubs (component, stubs, _Vue, shouldProxy) { const originalComponents = vm.$options.components const createElement = (el, ...args) => { - const comp = stubComponent( + const element = resolveElement( el, vm.$options, componentStubs, @@ -85,7 +86,7 @@ export function addStubs (component, stubs, _Vue, shouldProxy) { stubComponents ) - return originalCreateElement(comp, ...args) + return originalCreateElement(element, ...args) } vm._c = createElement diff --git a/test/specs/wrapper/trigger.spec.js b/test/specs/wrapper/trigger.spec.js index 9f6a4e477..07ce8ea5e 100644 --- a/test/specs/wrapper/trigger.spec.js +++ b/test/specs/wrapper/trigger.spec.js @@ -141,7 +141,6 @@ describeWithShallowAndMount('trigger', mountingMethod => { } } const wrapper = mountingMethod(TestComponent) - console.log(wrapper.html()) wrapper.findAll('button').trigger('click') } ) From 155aec5c4af1984cd1ed41bf6b9ec09117c03058 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 15:46:17 +0000 Subject: [PATCH 03/17] Move extending logic to createElementPatch --- packages/create-instance/add-stubs.js | 42 +++++--- packages/create-instance/create-instance.js | 26 ----- .../extend-extended-components.js | 95 ------------------- test/specs/mount.spec.js | 25 ----- 4 files changed, 29 insertions(+), 159 deletions(-) delete mode 100644 packages/create-instance/extend-extended-components.js diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index b7bd35fab..3d2b430dc 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -20,17 +20,19 @@ function resolveElement ( options, componentStubs, originalComponents, - whitelist + whitelist, + shouldStub, + _Vue ) { if ( - isReservedTag(el) || + typeof el === 'string' && isReservedTag(el) || shouldNotBeStubbed(el, whitelist) || isAlreadyStubbed(el, componentStubs) ) { return el } - if (typeof el === 'function' || typeof el === 'object') { + if (typeof el === 'function' || typeof el === 'object' && shouldStub) { return createStubFromComponent(el, el.name || 'anonymous') } @@ -48,13 +50,25 @@ function resolveElement ( return el } - const stub = createStubFromComponent(original || {}, el) + let stub - componentStubs.add(el) + if (shouldStub) { + stub = createStubFromComponent(original || {}, el) + } else if ( + (typeof original === 'function' && + (original.options || original.functional) && + !(original instanceof _Vue)) || (original && original.extends) + ) { + stub = _Vue.extend(original.options) + stub.options.$_vueTestUtils_original = original + } - options.components = { - ...options.components, - [el]: stub + if (stub) { + options.components = { + ...options.components, + [el]: stub + } + componentStubs.add(el) } return el @@ -78,12 +92,16 @@ export function addStubs (component, stubs, _Vue, shouldProxy) { const originalComponents = vm.$options.components const createElement = (el, ...args) => { - const element = resolveElement( + let element = el + + element = resolveElement( el, vm.$options, componentStubs, originalComponents, - stubComponents + stubComponents, + shouldProxy, + _Vue ) return originalCreateElement(element, ...args) @@ -93,9 +111,7 @@ export function addStubs (component, stubs, _Vue, shouldProxy) { vm.$createElement = createElement } - if (shouldProxy) { - addHook(_Vue.options, 'beforeCreate', patchRender) - } + addHook(_Vue.options, 'beforeCreate', patchRender) addHook(_Vue.options, 'beforeMount', addStubComponentsMixin) // beforeCreate is for components created in node, which diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 67bc4ed7c..1be784c03 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -9,13 +9,11 @@ import { compileTemplate, compileTemplateForSlots } from 'shared/compile-template' -import { isRequiredComponent } from 'shared/validators' import extractInstanceOptions from './extract-instance-options' import createFunctionalComponent from './create-functional-component' import { componentNeedsCompiling, isPlainObject } from 'shared/validators' import { validateSlots } from './validate-slots' import createScopedSlots from './create-scoped-slots' -import { extendExtendedComponents } from './extend-extended-components' function vueExtendUnsupportedOption (option: string) { return `options.${option} is not supported for ` + @@ -77,29 +75,6 @@ export default function createInstance ( compileTemplate(component) } - // Replace globally registered components with components extended - // from localVue. - // Vue version must be 2.3 or greater, because of a bug resolving - // extended constructor options (https://github.com/vuejs/vue/issues/4976) - if (vueVersion > 2.2) { - for (const c in _Vue.options.components) { - if (!isRequiredComponent(c)) { - const comp = _Vue.options.components[c] - const options = comp.options ? comp.options : comp - const extendedComponent = _Vue.extend(options) - extendedComponent.options.$_vueTestUtils_original = comp - _Vue.component(c, extendedComponent) - } - } - } - - extendExtendedComponents( - component, - _Vue, - options.logModifiedComponents, - instanceOptions.components - ) - if (component.options) { component.options._base = _Vue } @@ -145,7 +120,6 @@ export default function createInstance ( const parentComponentOptions = options.parentComponent || {} parentComponentOptions.provide = options.provide parentComponentOptions.doNotStubRender = true - parentComponentOptions.name = 'parent' parentComponentOptions.render = function (h) { const slots = options.slots diff --git a/packages/create-instance/extend-extended-components.js b/packages/create-instance/extend-extended-components.js deleted file mode 100644 index a086b18ba..000000000 --- a/packages/create-instance/extend-extended-components.js +++ /dev/null @@ -1,95 +0,0 @@ -import { warn } from 'shared/util' -import { addHook } from './add-hook' - -function resolveComponents (options = {}, components = {}) { - let extendOptions = options.extendOptions - while (extendOptions) { - resolveComponents(extendOptions, components) - extendOptions = extendOptions.extendOptions - } - let extendsFrom = options.extends - while (extendsFrom) { - resolveComponents(extendsFrom, components) - extendsFrom = extendsFrom.extends - } - Object.keys(options.components || {}).forEach((c) => { - components[c] = options.components[c] - }) - return components -} - -function shouldExtend (component) { - while (component) { - if (component.extendOptions) { - return true - } - component = component.extends - } -} - -// Components created with Vue.extend are not created internally in Vue -// by extending a localVue constructor. To make sure they inherit -// properties add to a localVue constructor, we must create new components by -// extending the original extended components from the localVue constructor. -// We apply a global mixin that overwrites the components original -// components with the extended components when they are created. -export function extendExtendedComponents ( - component, - _Vue, - logModifiedComponents, - excludedComponents = { }, - stubAllComponents = false -) { - const extendedComponents = Object.create(null) - const components = resolveComponents(component) - - Object.keys(components).forEach(c => { - const comp = components[c] - const shouldExtendComponent = - (shouldExtend(comp) && - !excludedComponents[c]) || - stubAllComponents - if (shouldExtendComponent) { - if (logModifiedComponents) { - warn( - `The child component <${c}> has been modified to ensure ` + - `it is created with properties injected by Vue Test Utils. \n` + - `This is because the component was created with Vue.extend, ` + - `or uses the Vue Class Component decorator. \n` + - `Because the component has been modified, it is not possible ` + - `to find it with a component selector. To find the ` + - `component, you must stub it manually using the stubs mounting ` + - `option, or use a name or ref selector. \n` + - `You can hide this warning by setting the Vue Test Utils ` + - `config.logModifiedComponents option to false.` - ) - } - const extendedComp = _Vue.extend(comp.options) - // Used to identify component in a render tree - extendedComp.options.$_vueTestUtils_original = comp - extendedComponents[c] = extendedComp - } - // If a component has been replaced with an extended component - // all its child components must also be replaced. - extendExtendedComponents( - comp, - _Vue, - logModifiedComponents, - {}, - shouldExtendComponent - ) - }) - if (Object.keys(extendedComponents).length > 0) { - addHook(_Vue.options, 'beforeCreate', function addExtendedOverwrites () { - if ( - this.constructor.extendOptions === component || - this.$options.$_vueTestUtils_original === component - ) { - Object.assign( - this.$options.components, - extendedComponents - ) - } - }) - } -} diff --git a/test/specs/mount.spec.js b/test/specs/mount.spec.js index 1a7138529..9b7401c4b 100644 --- a/test/specs/mount.spec.js +++ b/test/specs/mount.spec.js @@ -229,31 +229,6 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => { }) }) - it('logs if component is extended', () => { - const msg = - `[vue-test-utils]: The child component has been modified to ensure ` + - `it is created with properties injected by Vue Test Utils. \n` + - `This is because the component was created with Vue.extend, ` + - `or uses the Vue Class Component decorator. \n` + - `Because the component has been modified, it is not possible ` + - `to find it with a component selector. To find the ` + - `component, you must stub it manually using the stubs mounting ` + - `option, or use a name or ref selector. \n` + - `You can hide this warning by setting the Vue Test Utils ` + - `config.logModifiedComponents option to false.` - const ChildComponent = Vue.extend({ - template: '' - }) - const TestComponent = { - template: '', - components: { - ChildComponent - } - } - mount(TestComponent) - expect(console.error).calledWith(msg) - }) - it('deletes mounting options before passing options to component', () => { const wrapper = mount( { From 7832166d6002a484866ceb68c8fd54f1e3c03d2b Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 16:27:48 +0000 Subject: [PATCH 04/17] refactor: --- packages/create-instance/add-stubs.js | 69 +++++++++++++++------------ 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index 3d2b430dc..976659246 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -6,7 +6,7 @@ import { resolveComponent } from 'shared/util' import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' -const shouldNotBeStubbed = (el, whitelist) => resolveComponent(el, whitelist) +const isWhitelisted = (el, whitelist) => resolveComponent(el, whitelist) const isAlreadyStubbed = (el, stubs) => stubs.has(el) const isDynamicComponent = cmp => { @@ -15,27 +15,31 @@ const isDynamicComponent = cmp => { !cmp.functional } +function createStub (shouldStub, component, _Vue, el) { + if (shouldStub) { + return createStubFromComponent(component || {}, el) + } + + if ( + (typeof component === 'function' && + (component.options || component.functional) && + !(component instanceof _Vue)) || + (component && component.extends) + ) { + const stub = _Vue.extend(component.options) + stub.options.$_vueTestUtils_original = component + return stub + } +} + function resolveElement ( el, options, componentStubs, originalComponents, - whitelist, shouldStub, _Vue ) { - if ( - typeof el === 'string' && isReservedTag(el) || - shouldNotBeStubbed(el, whitelist) || - isAlreadyStubbed(el, componentStubs) - ) { - return el - } - - if (typeof el === 'function' || typeof el === 'object' && shouldStub) { - return createStubFromComponent(el, el.name || 'anonymous') - } - let original = resolveComponent(el, originalComponents) if ( @@ -50,18 +54,7 @@ function resolveElement ( return el } - let stub - - if (shouldStub) { - stub = createStubFromComponent(original || {}, el) - } else if ( - (typeof original === 'function' && - (original.options || original.functional) && - !(original instanceof _Vue)) || (original && original.extends) - ) { - stub = _Vue.extend(original.options) - stub.options.$_vueTestUtils_original = original - } + const stub = createStub(shouldStub, original, _Vue, el) if (stub) { options.components = { @@ -74,6 +67,19 @@ function resolveElement ( return el } +function shouldNotBeStubbed (el, whitelist, existingStubs) { + return ( + typeof el === 'string' && isReservedTag(el) || + isWhitelisted(el, whitelist) || + isAlreadyStubbed(el, existingStubs) + ) +} + +function isConstructor (el) { + (typeof el === 'function' || + typeof el === 'object') +} + export function addStubs (component, stubs, _Vue, shouldProxy) { const stubComponents = createStubsFromStubsObject(component.components, stubs) @@ -92,14 +98,17 @@ export function addStubs (component, stubs, _Vue, shouldProxy) { const originalComponents = vm.$options.components const createElement = (el, ...args) => { - let element = el - - element = resolveElement( + if (shouldNotBeStubbed(el, stubComponents, componentStubs)) { + return originalCreateElement(el, ...args) + } + // isConstructor(el) && shouldProxy + // ? createStubFromComponent(el, el.name || 'anonymous') + // : + const element = resolveElement( el, vm.$options, componentStubs, originalComponents, - stubComponents, shouldProxy, _Vue ) From e24d0b902b310acbb38f874f09e49717427d2f04 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 16:51:28 +0000 Subject: [PATCH 05/17] fix: add typings --- packages/create-instance/add-stubs.js | 8 ++++---- packages/shared/util.js | 2 +- packages/shared/validators.js | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index 976659246..cf832e175 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -75,10 +75,10 @@ function shouldNotBeStubbed (el, whitelist, existingStubs) { ) } -function isConstructor (el) { - (typeof el === 'function' || - typeof el === 'object') -} +// function isConstructor (el) { +// (typeof el === 'function' || +// typeof el === 'object') +// } export function addStubs (component, stubs, _Vue, shouldProxy) { const stubComponents = createStubsFromStubsObject(component.components, stubs) diff --git a/packages/shared/util.js b/packages/shared/util.js index 334761359..4a6ef3c19 100644 --- a/packages/shared/util.js +++ b/packages/shared/util.js @@ -38,7 +38,7 @@ function hasOwnProperty (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop) } -export function resolveComponent (id, components) { +export function resolveComponent (id: string, components: Object) { /* istanbul ignore if */ if (typeof id !== 'string') { return diff --git a/packages/shared/validators.js b/packages/shared/validators.js index be0434bef..c24453a2d 100644 --- a/packages/shared/validators.js +++ b/packages/shared/validators.js @@ -97,8 +97,8 @@ export function isRequiredComponent (name: string): boolean { } function makeMap ( - str, - expectsLowerCase + str: string, + expectsLowerCase?: boolean ) { var map = Object.create(null) var list = str.split(',') @@ -106,8 +106,8 @@ function makeMap ( map[list[i]] = true } return expectsLowerCase - ? function (val) { return map[val.toLowerCase()] } - : function (val) { return map[val] } + ? function (val: string) { return map[val.toLowerCase()] } + : function (val: string) { return map[val] } } export const isHTMLTag = makeMap( @@ -133,4 +133,4 @@ export const isSVG = makeMap( true ) -export const isReservedTag = (tag) => isHTMLTag(tag) || isSVG(tag) +export const isReservedTag = (tag: string) => isHTMLTag(tag) || isSVG(tag) From 97dfad88e4961f7e9353e6f491a6c3e5d97498af Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 17:13:53 +0000 Subject: [PATCH 06/17] fix: handle inline constructor functions --- packages/create-instance/add-stubs.js | 38 ++++++++++++-------- test/specs/mount.spec.js | 17 +++++++++ test/specs/mounting-options/localVue.spec.js | 17 +++++++++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index cf832e175..1c4d44119 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -15,17 +15,19 @@ const isDynamicComponent = cmp => { !cmp.functional } +function shouldExtend (component, _Vue) { + return (typeof component === 'function' && + (component.options || component.functional) && + !(component instanceof _Vue)) || + (component && component.extends) +} + function createStub (shouldStub, component, _Vue, el) { if (shouldStub) { return createStubFromComponent(component || {}, el) } - if ( - (typeof component === 'function' && - (component.options || component.functional) && - !(component instanceof _Vue)) || - (component && component.extends) - ) { + if (shouldExtend(component, _Vue)) { const stub = _Vue.extend(component.options) stub.options.$_vueTestUtils_original = component return stub @@ -41,7 +43,6 @@ function resolveElement ( _Vue ) { let original = resolveComponent(el, originalComponents) - if ( original && original.options && @@ -75,10 +76,10 @@ function shouldNotBeStubbed (el, whitelist, existingStubs) { ) } -// function isConstructor (el) { -// (typeof el === 'function' || -// typeof el === 'object') -// } +function isConstructor (el) { + return (typeof el === 'function' || + typeof el === 'object') +} export function addStubs (component, stubs, _Vue, shouldProxy) { const stubComponents = createStubsFromStubsObject(component.components, stubs) @@ -101,9 +102,18 @@ export function addStubs (component, stubs, _Vue, shouldProxy) { if (shouldNotBeStubbed(el, stubComponents, componentStubs)) { return originalCreateElement(el, ...args) } - // isConstructor(el) && shouldProxy - // ? createStubFromComponent(el, el.name || 'anonymous') - // : + if (isConstructor(el)) { + if (shouldProxy) { + const elem = createStubFromComponent(el, el.name || 'anonymous') + return originalCreateElement(elem, ...args) + } + if (shouldExtend(el, _Vue)) { + const extended = _Vue.extend(el) + extended.options.$_vueTestUtils_original = el + return originalCreateElement(extended, ...args) + } + // return originalCreateElement(element, ...args) + } const element = resolveElement( el, vm.$options, diff --git a/test/specs/mount.spec.js b/test/specs/mount.spec.js index 9b7401c4b..41a8d3835 100644 --- a/test/specs/mount.spec.js +++ b/test/specs/mount.spec.js @@ -398,4 +398,21 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => { const wrapper = mount(Component, options) expect(wrapper.text()).to.equal('aBC') }) + + it('handles inline components', () => { + const ChildComponent = { + render (h) { + h('p', this.$route.params) + } + } + const TestComponent = { + render: h => h(ChildComponent) + } + const localVue = createLocalVue() + localVue.prototype.$route = {} + const wrapper = mount(TestComponent, { + localVue + }) + expect(wrapper.findAll(ChildComponent).length).to.equal(1) + }) }) diff --git a/test/specs/mounting-options/localVue.spec.js b/test/specs/mounting-options/localVue.spec.js index ab2b343c5..c14321909 100644 --- a/test/specs/mounting-options/localVue.spec.js +++ b/test/specs/mounting-options/localVue.spec.js @@ -174,4 +174,21 @@ describeWithMountingMethods('options.localVue', mountingMethod => { expect(fn).to.throw() .with.property('message', message) }) + + it('is applied to inline constructor functions', () => { + const ChildComponent = Vue.extend({ + render (h) { + h('p', this.$route.params) + } + }) + const TestComponent = { + render: h => h(ChildComponent) + } + const localVue = createLocalVue() + localVue.prototype.$route = {} + const wrapper = mountingMethod(TestComponent, { + localVue + }) + expect(wrapper.findAll(ChildComponent).length).to.equal(1) + }) }) From 7a7e8d5870185c9d4cf7d85bfb67781760669f08 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 17:17:58 +0000 Subject: [PATCH 07/17] test: add case for dynamic components --- test/specs/shallow-mount.spec.js | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/specs/shallow-mount.spec.js b/test/specs/shallow-mount.spec.js index 37d48889a..64a8eae8a 100644 --- a/test/specs/shallow-mount.spec.js +++ b/test/specs/shallow-mount.spec.js @@ -445,4 +445,42 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => { }) ).to.throw() }) + + it('stubs dynamic components', () => { + const ChildComponent = { + template: '
' + } + const TestComponent = { + template: ` +
+ + + + +
+ `, + components: { ChildComponent }, + + data () { + return { + dataComponent: ChildComponent + } + }, + + computed: { + computedComponent () { + return ChildComponent + } + }, + + methods: { + methodComponent () { + return ChildComponent + } + } + } + const wrapper = shallowMount(TestComponent) + expect(wrapper.text()).to.equal('') + expect(wrapper.findAll(ChildComponent).length).to.equal(4) + }) }) From 4856a7aaeee829fe15d8158a612c9dc0f739a88d Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 17:49:22 +0000 Subject: [PATCH 08/17] refactor: reword tests --- test/specs/mount.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/specs/mount.spec.js b/test/specs/mount.spec.js index 41a8d3835..cdbdfcef4 100644 --- a/test/specs/mount.spec.js +++ b/test/specs/mount.spec.js @@ -27,7 +27,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => { expect(wrapper.vm).to.be.an('object') }) - it('returns new VueWrapper with mounted Vue instance when root is functional component', () => { + it('returns new VueWrapper when root is functional component', () => { const FunctionalComponent = { functional: true, render (h) { @@ -47,7 +47,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => { expect(wrapper.findAll('p').length).to.equal(2) }) - it('returns new VueWrapper with mounted Vue instance with props, if passed as propsData', () => { + it('returns new VueWrapper with correct props data', () => { const prop1 = { test: 'TEST' } const wrapper = mount(ComponentWithProps, { propsData: { prop1 }}) expect(wrapper.vm).to.be.an('object') From f2ca1db4e4f1689234a9bc28fab404dd6d8f6ec9 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 17:56:42 +0000 Subject: [PATCH 09/17] test: unskip vee-validate test: --- test/specs/external-libraries.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/specs/external-libraries.spec.js b/test/specs/external-libraries.spec.js index 9027d454e..06b2a4537 100644 --- a/test/specs/external-libraries.spec.js +++ b/test/specs/external-libraries.spec.js @@ -3,7 +3,7 @@ import VeeValidate from 'vee-validate' import { describeWithShallowAndMount } from '~resources/utils' describeWithShallowAndMount('external libraries', () => { - it.skip('works with vee validate', () => { + it('works with vee validate', () => { const TestComponent = { template: '
' } From a38a265d1c77b1d78478dd86df9576c4f99c4839 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 18:41:22 +0000 Subject: [PATCH 10/17] refactor: seperate stub-components and patch-render --- packages/create-instance/add-mocks.js | 4 +- packages/create-instance/add-stubs.js | 129 +----------------- .../create-functional-component.js | 2 +- packages/create-instance/create-instance.js | 13 +- packages/create-instance/patch-render.js | 121 ++++++++++++++++ packages/shared/create-component-stubs.js | 4 +- 6 files changed, 137 insertions(+), 136 deletions(-) create mode 100644 packages/create-instance/patch-render.js diff --git a/packages/create-instance/add-mocks.js b/packages/create-instance/add-mocks.js index 9b5a6cfee..cf0801a5c 100644 --- a/packages/create-instance/add-mocks.js +++ b/packages/create-instance/add-mocks.js @@ -3,8 +3,8 @@ import $$Vue from 'vue' import { warn } from 'shared/util' export default function addMocks ( - mockedProperties: Object | false = {}, - Vue: Component + Vue: Component, + mockedProperties: Object | false = {} ): void { if (mockedProperties === false) { return diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index 1c4d44119..e4049c1df 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -1,137 +1,10 @@ -import { - createStubsFromStubsObject, - createStubFromComponent -} from 'shared/create-component-stubs' -import { resolveComponent } from 'shared/util' -import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' -const isWhitelisted = (el, whitelist) => resolveComponent(el, whitelist) -const isAlreadyStubbed = (el, stubs) => stubs.has(el) - -const isDynamicComponent = cmp => { - return typeof cmp === 'function' && - !cmp.options && - !cmp.functional -} - -function shouldExtend (component, _Vue) { - return (typeof component === 'function' && - (component.options || component.functional) && - !(component instanceof _Vue)) || - (component && component.extends) -} - -function createStub (shouldStub, component, _Vue, el) { - if (shouldStub) { - return createStubFromComponent(component || {}, el) - } - - if (shouldExtend(component, _Vue)) { - const stub = _Vue.extend(component.options) - stub.options.$_vueTestUtils_original = component - return stub - } -} - -function resolveElement ( - el, - options, - componentStubs, - originalComponents, - shouldStub, - _Vue -) { - let original = resolveComponent(el, originalComponents) - if ( - original && - original.options && - original.options.$_vueTestUtils_original - ) { - original = original.options.$_vueTestUtils_original - } - - if (isDynamicComponent(original)) { - return el - } - - const stub = createStub(shouldStub, original, _Vue, el) - - if (stub) { - options.components = { - ...options.components, - [el]: stub - } - componentStubs.add(el) - } - - return el -} - -function shouldNotBeStubbed (el, whitelist, existingStubs) { - return ( - typeof el === 'string' && isReservedTag(el) || - isWhitelisted(el, whitelist) || - isAlreadyStubbed(el, existingStubs) - ) -} - -function isConstructor (el) { - return (typeof el === 'function' || - typeof el === 'object') -} - -export function addStubs (component, stubs, _Vue, shouldProxy) { - const stubComponents = createStubsFromStubsObject(component.components, stubs) - +export function addStubs (_Vue, stubComponents) { function addStubComponentsMixin () { Object.assign(this.$options.components, stubComponents) } - function patchRender () { - const vm = this - if (vm.$options.doNotStubRender || vm._isFunctionalContainer) { - return - } - - const componentStubs = new Set() - const originalCreateElement = vm.$createElement - const originalComponents = vm.$options.components - - const createElement = (el, ...args) => { - if (shouldNotBeStubbed(el, stubComponents, componentStubs)) { - return originalCreateElement(el, ...args) - } - if (isConstructor(el)) { - if (shouldProxy) { - const elem = createStubFromComponent(el, el.name || 'anonymous') - return originalCreateElement(elem, ...args) - } - if (shouldExtend(el, _Vue)) { - const extended = _Vue.extend(el) - extended.options.$_vueTestUtils_original = el - return originalCreateElement(extended, ...args) - } - // return originalCreateElement(element, ...args) - } - const element = resolveElement( - el, - vm.$options, - componentStubs, - originalComponents, - shouldProxy, - _Vue - ) - - return originalCreateElement(element, ...args) - } - - vm._c = createElement - vm.$createElement = createElement - } - - addHook(_Vue.options, 'beforeCreate', patchRender) - addHook(_Vue.options, 'beforeMount', addStubComponentsMixin) // beforeCreate is for components created in node, which // never mount diff --git a/packages/create-instance/create-functional-component.js b/packages/create-instance/create-functional-component.js index c05ed3b6f..59cb0fb7e 100644 --- a/packages/create-instance/create-functional-component.js +++ b/packages/create-instance/create-functional-component.js @@ -21,7 +21,7 @@ export default function createFunctionalComponent ( data.scopedSlots = createScopedSlots(mountingOptions.scopedSlots) return { - doNotStubRender: true, + $_doNotStubChildren: true, render (h: Function) { return h( component, diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 1be784c03..eadc7f546 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -14,6 +14,8 @@ import createFunctionalComponent from './create-functional-component' import { componentNeedsCompiling, isPlainObject } from 'shared/validators' import { validateSlots } from './validate-slots' import createScopedSlots from './create-scoped-slots' +import { createStubsFromStubsObject } from 'shared/create-component-stubs' +import { patchRender } from './patch-render' function vueExtendUnsupportedOption (option: string) { return `options.${option} is not supported for ` + @@ -54,10 +56,15 @@ export default function createInstance ( // instance options are options that are passed to the // root instance when it's instantiated const instanceOptions = extractInstanceOptions(options) + const stubComponentsObject = createStubsFromStubsObject( + component.components, + options.stubs + ) addEventLogger(_Vue) - addMocks(options.mocks, _Vue) - addStubs(component, options.stubs, _Vue, options.shouldProxy) + addMocks(_Vue, options.mocks) + addStubs(_Vue, stubComponentsObject) + patchRender(_Vue, stubComponentsObject, options.shouldProxy) if ( (component.options && component.options.functional) || @@ -119,7 +126,7 @@ export default function createInstance ( const parentComponentOptions = options.parentComponent || {} parentComponentOptions.provide = options.provide - parentComponentOptions.doNotStubRender = true + parentComponentOptions.$_doNotStubChildren = true parentComponentOptions.render = function (h) { const slots = options.slots diff --git a/packages/create-instance/patch-render.js b/packages/create-instance/patch-render.js new file mode 100644 index 000000000..12c3f3494 --- /dev/null +++ b/packages/create-instance/patch-render.js @@ -0,0 +1,121 @@ +import { + createStubFromComponent +} from 'shared/create-component-stubs' +import { resolveComponent } from 'shared/util' +import { isReservedTag } from 'shared/validators' +import { addHook } from './add-hook' + +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' && + (component.options || component.functional) && + !(component instanceof _Vue)) || + (component && component.extends) +} + +function createStub (shouldStub, component, _Vue, el) { + if (shouldStub) { + return createStubFromComponent(component || {}, el) + } + + if (shouldExtend(component, _Vue)) { + const stub = _Vue.extend(component.options) + stub.options.$_vueTestUtils_original = component + return stub + } +} + +function resolveElement ( + el, + options, + componentStubs, + originalComponents, + shouldStub, + _Vue +) { + let original = resolveComponent(el, originalComponents) + if ( + original && + original.options && + original.options.$_vueTestUtils_original + ) { + original = original.options.$_vueTestUtils_original + } + + if (isDynamicComponent(original)) { + return el + } + + const stub = createStub(shouldStub, original, _Vue, el) + + if (stub) { + options.components = { + ...options.components, + [el]: stub + } + componentStubs.add(el) + } + + return el +} + +function shouldNotBeStubbed (el, whitelist, existingStubs) { + return ( + typeof el === 'string' && isReservedTag(el) || + isWhitelisted(el, whitelist) || + isAlreadyStubbed(el, existingStubs) + ) +} + +function isConstructor (el) { + return (typeof el === 'function') +} + +export function patchRender (_Vue, stubs, stubAllComponents) { + function patchRenderMixin () { + const vm = this + + if (vm.$options.$_doNotStubChildren || vm._isFunctionalContainer) { + return + } + + const modifiedComponents = new Set() + const originalCreateElement = vm.$createElement + const originalComponents = vm.$options.components + + const createElement = (el, ...args) => { + if (shouldNotBeStubbed(el, stubs, modifiedComponents)) { + return originalCreateElement(el, ...args) + } + if (isConstructor(el)) { + if (stubAllComponents) { + const elem = createStubFromComponent(el, el.name || 'anonymous') + return originalCreateElement(elem, ...args) + } + if (shouldExtend(el, _Vue)) { + const extended = _Vue.extend(el) + extended.options.$_vueTestUtils_original = el + return originalCreateElement(extended, ...args) + } + } + const element = resolveElement( + el, + vm.$options, + modifiedComponents, + originalComponents, + stubAllComponents, + _Vue + ) + + return originalCreateElement(element, ...args) + } + + vm._c = createElement + vm.$createElement = createElement + } + + addHook(_Vue.options, 'beforeCreate', patchRenderMixin) +} diff --git a/packages/shared/create-component-stubs.js b/packages/shared/create-component-stubs.js index 6d720d65b..82e034610 100644 --- a/packages/shared/create-component-stubs.js +++ b/packages/shared/create-component-stubs.js @@ -81,7 +81,7 @@ export function createStubFromComponent ( return { ...getCoreProperties(componentOptions), $_vueTestUtils_original: originalComponent, - doNotStubRender: true, + $_doNotStubChildren: true, render (h, context) { return h( tagName, @@ -118,7 +118,7 @@ export function createStubFromString ( return { ...getCoreProperties(componentOptions), - doNotStubRender: true, + $_doNotStubChildren: true, ...compileFromString(templateString) } } From 9663793c9954c75072e532c8a3ed6e884e38fb43 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 19:13:07 +0000 Subject: [PATCH 11/17] refactor: add comment to patchRender function --- packages/create-instance/patch-render.js | 112 ++++++++++++----------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/packages/create-instance/patch-render.js b/packages/create-instance/patch-render.js index 12c3f3494..3a4a26318 100644 --- a/packages/create-instance/patch-render.js +++ b/packages/create-instance/patch-render.js @@ -16,57 +16,27 @@ function shouldExtend (component, _Vue) { (component && component.extends) } -function createStub (shouldStub, component, _Vue, el) { +function extend (component, _Vue) { + const stub = _Vue.extend(component.options) + stub.options.$_vueTestUtils_original = component + return stub +} + +function createStubIfNeeded (shouldStub, component, _Vue, el) { if (shouldStub) { return createStubFromComponent(component || {}, el) } if (shouldExtend(component, _Vue)) { - const stub = _Vue.extend(component.options) - stub.options.$_vueTestUtils_original = component - return stub + return extend(component, _Vue) } } -function resolveElement ( - el, - options, - componentStubs, - originalComponents, - shouldStub, - _Vue -) { - let original = resolveComponent(el, originalComponents) - if ( - original && - original.options && - original.options.$_vueTestUtils_original - ) { - original = original.options.$_vueTestUtils_original - } - - if (isDynamicComponent(original)) { - return el - } - - const stub = createStub(shouldStub, original, _Vue, el) - - if (stub) { - options.components = { - ...options.components, - [el]: stub - } - componentStubs.add(el) - } - - return el -} - -function shouldNotBeStubbed (el, whitelist, existingStubs) { +function shouldNotBeStubbed (el, whitelist, modifiedComponents) { return ( typeof el === 'string' && isReservedTag(el) || isWhitelisted(el, whitelist) || - isAlreadyStubbed(el, existingStubs) + isAlreadyStubbed(el, modifiedComponents) ) } @@ -75,6 +45,12 @@ function isConstructor (el) { } export function patchRender (_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 + // component constructors were created from the _Vue constructor. If not, + // we must replace them with components created from the _Vue constructor + // before calling the original $createElement. This ensures that the component + // will have the correct instance properties and stubs when it renders. function patchRenderMixin () { const vm = this @@ -90,27 +66,55 @@ export function patchRender (_Vue, stubs, stubAllComponents) { if (shouldNotBeStubbed(el, stubs, modifiedComponents)) { return originalCreateElement(el, ...args) } + if (isConstructor(el)) { if (stubAllComponents) { - const elem = createStubFromComponent(el, el.name || 'anonymous') + const elem = createStubFromComponent( + el, + el.name || 'anonymous' + ) return originalCreateElement(elem, ...args) } + if (shouldExtend(el, _Vue)) { - const extended = _Vue.extend(el) - extended.options.$_vueTestUtils_original = el - return originalCreateElement(extended, ...args) + return originalCreateElement(extend(el, _Vue), ...args) + } + + return originalCreateElement(el, ...args) + } + + if (typeof el === 'string') { + let original = resolveComponent(el, originalComponents) + + if ( + original && + original.options && + original.options.$_vueTestUtils_original + ) { + original = original.options.$_vueTestUtils_original + } + + if (isDynamicComponent(original)) { + return originalCreateElement(el, ...args) + } + + const stub = createStubIfNeeded( + stubAllComponents, + original, + _Vue, + el + ) + + if (stub) { + vm.$options.components = { + ...vm.$options.components, + [el]: stub + } + modifiedComponents.add(el) } } - const element = resolveElement( - el, - vm.$options, - modifiedComponents, - originalComponents, - stubAllComponents, - _Vue - ) - - return originalCreateElement(element, ...args) + + return originalCreateElement(el, ...args) } vm._c = createElement From 2cc481b1669ef083bb8a0cc05a5e45a560493c56 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 19:43:23 +0000 Subject: [PATCH 12/17] refactor: small tweaks --- packages/create-instance/add-mocks.js | 6 +++--- .../create-functional-component.js | 1 - packages/create-instance/create-instance.js | 1 - packages/create-instance/patch-render.js | 16 ++++++++-------- packages/shared/util.js | 7 +------ test/resources/utils.js | 4 ++-- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/packages/create-instance/add-mocks.js b/packages/create-instance/add-mocks.js index cf0801a5c..ef83b5e14 100644 --- a/packages/create-instance/add-mocks.js +++ b/packages/create-instance/add-mocks.js @@ -3,7 +3,7 @@ import $$Vue from 'vue' import { warn } from 'shared/util' export default function addMocks ( - Vue: Component, + _Vue: Component, mockedProperties: Object | false = {} ): void { if (mockedProperties === false) { @@ -12,7 +12,7 @@ export default function addMocks ( Object.keys(mockedProperties).forEach(key => { try { // $FlowIgnore - Vue.prototype[key] = mockedProperties[key] + _Vue.prototype[key] = mockedProperties[key] } catch (e) { warn( `could not overwrite property ${key}, this is ` + @@ -21,6 +21,6 @@ export default function addMocks ( ) } // $FlowIgnore - $$Vue.util.defineReactive(Vue, key, mockedProperties[key]) + $$Vue.util.defineReactive(_Vue, key, mockedProperties[key]) }) } diff --git a/packages/create-instance/create-functional-component.js b/packages/create-instance/create-functional-component.js index 59cb0fb7e..61d07aaf9 100644 --- a/packages/create-instance/create-functional-component.js +++ b/packages/create-instance/create-functional-component.js @@ -21,7 +21,6 @@ export default function createFunctionalComponent ( data.scopedSlots = createScopedSlots(mountingOptions.scopedSlots) return { - $_doNotStubChildren: true, render (h: Function) { return h( component, diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index eadc7f546..5bb103671 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -136,7 +136,6 @@ export default function createInstance ( Constructor, { ref: 'vm', - name: 'root', on: options.listeners, attrs: { ...options.attrs, diff --git a/packages/create-instance/patch-render.js b/packages/create-instance/patch-render.js index 3a4a26318..0d4759a45 100644 --- a/packages/create-instance/patch-render.js +++ b/packages/create-instance/patch-render.js @@ -49,8 +49,8 @@ export function patchRender (_Vue, stubs, stubAllComponents) { // before they are rendered in shallow mode. We also need to ensure that // component constructors were created from the _Vue constructor. If not, // we must replace them with components created from the _Vue constructor - // before calling the original $createElement. This ensures that the component - // will have the correct instance properties and stubs when it renders. + // before calling the original $createElement. This ensures that components + // have the correct instance properties and stubs when they are rendered. function patchRenderMixin () { const vm = this @@ -69,18 +69,18 @@ export function patchRender (_Vue, stubs, stubAllComponents) { if (isConstructor(el)) { if (stubAllComponents) { - const elem = createStubFromComponent( + const stub = createStubFromComponent( el, el.name || 'anonymous' ) - return originalCreateElement(elem, ...args) + return originalCreateElement(stub, ...args) } - if (shouldExtend(el, _Vue)) { - return originalCreateElement(extend(el, _Vue), ...args) - } + const Constructor = shouldExtend(el, _Vue) + ? extend(el, _Vue) + : el - return originalCreateElement(el, ...args) + return originalCreateElement(Constructor, ...args) } if (typeof el === 'string') { diff --git a/packages/shared/util.js b/packages/shared/util.js index 4a6ef3c19..3c4df8035 100644 --- a/packages/shared/util.js +++ b/packages/shared/util.js @@ -39,12 +39,9 @@ function hasOwnProperty (obj, prop) { } export function resolveComponent (id: string, components: Object) { - /* istanbul ignore if */ if (typeof id !== 'string') { return } - // var components = options['components']; - // check local registration variations first if (hasOwnProperty(components, id)) { return components[id] @@ -58,7 +55,5 @@ export function resolveComponent (id: string, components: Object) { return components[PascalCaseId] } // fallback to prototype chain - var res = - components[id] || components[camelizedId] || components[PascalCaseId] - return res + return components[id] || components[camelizedId] || components[PascalCaseId] } diff --git a/test/resources/utils.js b/test/resources/utils.js index d926ddbdf..eabb68c92 100644 --- a/test/resources/utils.js +++ b/test/resources/utils.js @@ -29,9 +29,9 @@ export const functionalSFCsSupported = vueVersion > 2.4 export const scopedSlotsSupported = vueVersion > 2 const shallowAndMount = - process.env.TEST_ENV === 'node' ? [] : [shallowMount, mount] + process.env.TEST_ENV === 'node' ? [] : [mount, shallowMount] const shallowMountAndRender = - process.env.TEST_ENV === 'node' ? [renderToString] : [shallowMount, mount] + process.env.TEST_ENV === 'node' ? [renderToString] : [mount, shallowMount] export function describeWithShallowAndMount (spec, cb) { if (shallowAndMount.length > 0) { From 25212beebe897f7fd2a97cf0bc8353b87cd00328 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 20:07:00 +0000 Subject: [PATCH 13/17] test: fix tests --- packages/create-instance/create-instance.js | 1 + test/specs/mounting-options/localVue.spec.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 5bb103671..44d99f4d9 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -58,6 +58,7 @@ export default function createInstance ( const instanceOptions = extractInstanceOptions(options) const stubComponentsObject = createStubsFromStubsObject( component.components, + // $FlowIgnore options.stubs ) diff --git a/test/specs/mounting-options/localVue.spec.js b/test/specs/mounting-options/localVue.spec.js index c14321909..05e7ba1b7 100644 --- a/test/specs/mounting-options/localVue.spec.js +++ b/test/specs/mounting-options/localVue.spec.js @@ -189,6 +189,9 @@ describeWithMountingMethods('options.localVue', mountingMethod => { const wrapper = mountingMethod(TestComponent, { localVue }) + if (mountingMethod.name === 'renderToString') { + return + } expect(wrapper.findAll(ChildComponent).length).to.equal(1) }) }) From 050e884fd1076cf6a62c44ec38d0d86b4084a875 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 20:49:41 +0000 Subject: [PATCH 14/17] fix: bump vee-vuelidate --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 487c330aa..c4b968403 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "sinon": "^2.3.2", "sinon-chai": "^2.10.0", "typescript": "^3.0.1", - "vee-validate": "2.1.0-beta.5", + "vee-validate": "^2.1.3", "vue": "2.5.16", "vue-class-component": "^6.1.2", "vue-loader": "^13.6.2", diff --git a/yarn.lock b/yarn.lock index 382028547..2e4fb3af1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9720,9 +9720,9 @@ vary@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" -vee-validate@2.1.0-beta.5: - version "2.1.0-beta.5" - resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.1.0-beta.5.tgz#311c4629face2383964beb06768b379500f25a69" +vee-validate@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.1.3.tgz#c221d9718bdbf34828bda1ac373bd808a68ba4da" vendors@^1.0.0: version "1.0.1" From 43f2e74eb49eaa75195e3e6dd72cf78ec2d62629 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 24 Nov 2018 21:51:36 +0000 Subject: [PATCH 15/17] fix: handle Vue 2.0 --- package.json | 2 +- packages/create-instance/patch-render.js | 9 +++-- test/specs/mounting-options/localVue.spec.js | 38 ++++++++++---------- test/specs/shallow-mount.spec.js | 12 ++----- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index c4b968403..b4c34e494 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "lint:fix": "npm run lint -- --fix", "release": "npm run build && npm run test:unit:only && lerna publish --conventional-commits -m \"chore(release): publish %s\" --cd-version prerelease", "test": "npm run lint && npm run lint:docs && npm run flow && npm run test:types && npm run test:unit && npm run test:unit:karma && npm run test:unit:node", - "test:compat": "scripts/test-compat.sh", + "test:compat": "scripts/test-compat-all.sh", "test:unit": "npm run build:test && npm run test:unit:only", "test:unit:only": "mocha-webpack --webpack-config test/setup/webpack.test.config.js test/specs --recursive --require test/setup/mocha.setup.js", "test:unit:debug": "npm run build:test && node --inspect-brk node_modules/.bin/mocha-webpack --webpack-config test/setup/webpack.test.config.js test/specs --recursive --require test/setup/mocha.setup.js", diff --git a/packages/create-instance/patch-render.js b/packages/create-instance/patch-render.js index 0d4759a45..6fe45038e 100644 --- a/packages/create-instance/patch-render.js +++ b/packages/create-instance/patch-render.js @@ -1,7 +1,7 @@ import { createStubFromComponent } from 'shared/create-component-stubs' -import { resolveComponent } from 'shared/util' +import { resolveComponent, vueVersion } from 'shared/util' import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' @@ -9,6 +9,9 @@ const isWhitelisted = (el, whitelist) => resolveComponent(el, whitelist) const isAlreadyStubbed = (el, stubs) => stubs.has(el) const isDynamicComponent = cmp => typeof cmp === 'function' && !cmp.cid +const CREATE_ELEMENT_ALIAS = vueVersion < 2.1 ? '_h' : '_c' +const LIFECYCLE_HOOK = vueVersion < 2.1 ? 'beforeMount' : 'beforeCreate' + function shouldExtend (component, _Vue) { return (typeof component === 'function' && (component.options || component.functional) && @@ -117,9 +120,9 @@ export function patchRender (_Vue, stubs, stubAllComponents) { return originalCreateElement(el, ...args) } - vm._c = createElement + vm[CREATE_ELEMENT_ALIAS] = createElement vm.$createElement = createElement } - addHook(_Vue.options, 'beforeCreate', patchRenderMixin) + addHook(_Vue.options, LIFECYCLE_HOOK, patchRenderMixin) } diff --git a/test/specs/mounting-options/localVue.spec.js b/test/specs/mounting-options/localVue.spec.js index 05e7ba1b7..38e9392cc 100644 --- a/test/specs/mounting-options/localVue.spec.js +++ b/test/specs/mounting-options/localVue.spec.js @@ -5,7 +5,7 @@ import { vueVersion } from '~resources/utils' import { createLocalVue } from '~vue/test-utils' -import { itSkipIf, itRunIf } from 'conditional-specs' +import { itSkipIf, itRunIf, itDoNotRunIf } from 'conditional-specs' import Vuex from 'vuex' describeWithMountingMethods('options.localVue', mountingMethod => { @@ -175,23 +175,25 @@ describeWithMountingMethods('options.localVue', mountingMethod => { .with.property('message', message) }) - it('is applied to inline constructor functions', () => { - const ChildComponent = Vue.extend({ - render (h) { - h('p', this.$route.params) + itDoNotRunIf( + vueVersion < 2.3, + 'is applied to inline constructor functions', () => { + const ChildComponent = Vue.extend({ + render (h) { + h('p', this.$route.params) + } + }) + const TestComponent = { + render: h => h(ChildComponent) } + const localVue = createLocalVue() + localVue.prototype.$route = {} + const wrapper = mountingMethod(TestComponent, { + localVue + }) + if (mountingMethod.name === 'renderToString') { + return + } + expect(wrapper.findAll(ChildComponent).length).to.equal(1) }) - const TestComponent = { - render: h => h(ChildComponent) - } - const localVue = createLocalVue() - localVue.prototype.$route = {} - const wrapper = mountingMethod(TestComponent, { - localVue - }) - if (mountingMethod.name === 'renderToString') { - return - } - expect(wrapper.findAll(ChildComponent).length).to.equal(1) - }) }) diff --git a/test/specs/shallow-mount.spec.js b/test/specs/shallow-mount.spec.js index 64a8eae8a..d4c65cf47 100644 --- a/test/specs/shallow-mount.spec.js +++ b/test/specs/shallow-mount.spec.js @@ -29,15 +29,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => { expect(wrapper.vm).to.be.an('object') }) - it('returns new VueWrapper of Vue localVue with all children stubbed', () => { - const wrapper = shallowMount(ComponentWithNestedChildren) - expect(wrapper.isVueInstance()).to.equal(true) - expect(wrapper.findAll(Component).length).to.equal(0) - - expect(wrapper.findAll(ComponentWithChild).length).to.equal(1) - }) - - it('returns new VueWrapper of Vue localVue with all children stubbed', () => { + it('returns new VueWrapper with all children stubbed', () => { const wrapper = shallowMount(ComponentWithNestedChildren) expect(wrapper.isVueInstance()).to.equal(true) expect(wrapper.findAll(Component).length).to.equal(0) @@ -89,7 +81,7 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => {

Hello

World

-
+ ` } const wrapper = shallowMount(TestComponent, { From d123d144dab3420907c27cf2ae3bb01618d5b562 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sun, 25 Nov 2018 07:16:48 +0000 Subject: [PATCH 16/17] Use semver check --- packages/create-instance/patch-render.js | 11 ++++++++--- packages/shared/util.js | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/create-instance/patch-render.js b/packages/create-instance/patch-render.js index 6fe45038e..551841749 100644 --- a/packages/create-instance/patch-render.js +++ b/packages/create-instance/patch-render.js @@ -1,16 +1,21 @@ import { createStubFromComponent } from 'shared/create-component-stubs' -import { resolveComponent, vueVersion } from 'shared/util' +import { resolveComponent, semVerGreaterThan } from 'shared/util' import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' +import Vue from 'vue' const isWhitelisted = (el, whitelist) => resolveComponent(el, whitelist) const isAlreadyStubbed = (el, stubs) => stubs.has(el) const isDynamicComponent = cmp => typeof cmp === 'function' && !cmp.cid -const CREATE_ELEMENT_ALIAS = vueVersion < 2.1 ? '_h' : '_c' -const LIFECYCLE_HOOK = vueVersion < 2.1 ? 'beforeMount' : 'beforeCreate' +const CREATE_ELEMENT_ALIAS = semVerGreaterThan(Vue.version, '2.1.5') + ? '_c' + : '_h' +const LIFECYCLE_HOOK = semVerGreaterThan(Vue.version, '2.1.8') + ? 'beforeCreate' + : 'beforeMount' function shouldExtend (component, _Vue) { return (typeof component === 'function' && diff --git a/packages/shared/util.js b/packages/shared/util.js index 3c4df8035..e159385b7 100644 --- a/packages/shared/util.js +++ b/packages/shared/util.js @@ -10,6 +10,7 @@ export function warn (msg: string): void { } const camelizeRE = /-(\w)/g + export const camelize = (str: string): string => { const camelizedStr = str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '' @@ -57,3 +58,17 @@ export function resolveComponent (id: string, components: Object) { // fallback to prototype chain return components[id] || components[camelizedId] || components[PascalCaseId] } + +export function semVerGreaterThan (a: string, b: string) { + const pa = a.split('.') + const pb = b.split('.') + for (let i = 0; i < 3; i++) { + var na = Number(pa[i]) + var nb = Number(pb[i]) + if (na > nb) return true + if (nb > na) return false + if (!isNaN(na) && isNaN(nb)) return true + if (isNaN(na) && !isNaN(nb)) return false + } + return false +} From 9f865bb70e839b09f0273bee5111b4bf9dfbc0d0 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sun, 25 Nov 2018 07:40:57 +0000 Subject: [PATCH 17/17] refactor: check is not dynamic component --- packages/create-instance/patch-render.js | 32 ++++++++---------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/create-instance/patch-render.js b/packages/create-instance/patch-render.js index 551841749..b30b774bf 100644 --- a/packages/create-instance/patch-render.js +++ b/packages/create-instance/patch-render.js @@ -1,6 +1,4 @@ -import { - createStubFromComponent -} from 'shared/create-component-stubs' +import { createStubFromComponent } from 'shared/create-component-stubs' import { resolveComponent, semVerGreaterThan } from 'shared/util' import { isReservedTag } from 'shared/validators' import { addHook } from './add-hook' @@ -18,10 +16,10 @@ const LIFECYCLE_HOOK = semVerGreaterThan(Vue.version, '2.1.8') : 'beforeMount' function shouldExtend (component, _Vue) { - return (typeof component === 'function' && - (component.options || component.functional) && - !(component instanceof _Vue)) || - (component && component.extends) + return ( + (typeof component === 'function' && !isDynamicComponent(component)) || + (component && component.extends) + ) } function extend (component, _Vue) { @@ -42,14 +40,14 @@ function createStubIfNeeded (shouldStub, component, _Vue, el) { function shouldNotBeStubbed (el, whitelist, modifiedComponents) { return ( - typeof el === 'string' && isReservedTag(el) || + (typeof el === 'string' && isReservedTag(el)) || isWhitelisted(el, whitelist) || isAlreadyStubbed(el, modifiedComponents) ) } function isConstructor (el) { - return (typeof el === 'function') + return typeof el === 'function' } export function patchRender (_Vue, stubs, stubAllComponents) { @@ -77,16 +75,11 @@ export function patchRender (_Vue, stubs, stubAllComponents) { if (isConstructor(el)) { if (stubAllComponents) { - const stub = createStubFromComponent( - el, - el.name || 'anonymous' - ) + const stub = createStubFromComponent(el, el.name || 'anonymous') return originalCreateElement(stub, ...args) } - const Constructor = shouldExtend(el, _Vue) - ? extend(el, _Vue) - : el + const Constructor = shouldExtend(el, _Vue) ? extend(el, _Vue) : el return originalCreateElement(Constructor, ...args) } @@ -106,12 +99,7 @@ export function patchRender (_Vue, stubs, stubAllComponents) { return originalCreateElement(el, ...args) } - const stub = createStubIfNeeded( - stubAllComponents, - original, - _Vue, - el - ) + const stub = createStubIfNeeded(stubAllComponents, original, _Vue, el) if (stub) { vm.$options.components = {