From 3a476b3b10748407d4a0a80fed958868c91543d3 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 21 Jul 2018 06:36:57 +0100 Subject: [PATCH 01/13] Extend all extended components --- packages/create-instance/create-instance.js | 49 +++++---------- .../extend-extended-components.js | 62 +++++++++++++++++++ .../extract-instance-options.js | 9 ++- packages/test-utils/src/shallow-mount.js | 4 +- test/specs/mounting-options/localVue.spec.js | 3 +- test/specs/mounting-options/slots.spec.js | 12 ++-- test/specs/mounting-options/stubs.spec.js | 3 +- test/specs/wrapper/emitted.spec.js | 56 +++++++++++++++++ 8 files changed, 150 insertions(+), 48 deletions(-) create mode 100644 packages/create-instance/extend-extended-components.js diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 515ddeff9..f7e901579 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -4,13 +4,14 @@ import { createSlotVNodes } from './add-slots' import addMocks from './add-mocks' import { addEventLogger } from './log-events' import { createComponentStubs } from 'shared/stub-components' -import { throwError, warn, vueVersion } from 'shared/util' +import { throwError, vueVersion } from 'shared/util' import { compileTemplate } from 'shared/compile-template' import extractInstanceOptions from './extract-instance-options' import createFunctionalComponent from './create-functional-component' import { componentNeedsCompiling } from 'shared/validators' import { validateSlots } from './validate-slots' import createScopedSlots from './create-scoped-slots' +import { extendExtendedComponents } from './extend-extended-components' export default function createInstance ( component: Component, @@ -27,9 +28,6 @@ export default function createInstance ( // root instance when it's instantiated // // component options are the root components options - const componentOptions = typeof component === 'function' - ? component.extendOptions - : component const instanceOptions = extractInstanceOptions(options) @@ -59,13 +57,15 @@ export default function createInstance ( // $FlowIgnore options.stubs ) - if (options.stubs) { - instanceOptions.components = { - ...instanceOptions.components, - // $FlowIgnore - ...stubComponents - } - } + + extendExtendedComponents( + component, + _Vue, + options.logModifiedComponents, + instanceOptions.components + ) + + // stub components should override every component defined in a component _Vue.mixin({ created () { Object.assign( @@ -74,26 +74,6 @@ export default function createInstance ( ) } }) - Object.keys(componentOptions.components || {}).forEach(c => { - if ( - componentOptions.components[c].extendOptions && - !instanceOptions.components[c] - ) { - if (options.logModifiedComponents) { - warn( - `an extended child component <${c}> has been modified ` + - `to ensure it has the correct instance properties. ` + - `This means it is not possible to find the component ` + - `with a component selector. To find the component, ` + - `you must stub it manually using the stubs mounting ` + - `option.` - ) - } - instanceOptions.components[c] = _Vue.extend( - componentOptions.components[c] - ) - } - }) if (component.options) { component.options._base = _Vue @@ -103,9 +83,10 @@ export default function createInstance ( ? component.extend(instanceOptions) : _Vue.extend(component).extend(instanceOptions) - Object.keys(instanceOptions.components || {}).forEach(key => { - Constructor.component(key, instanceOptions.components[key]) - _Vue.component(key, instanceOptions.components[key]) + Constructor._vueTestUtilsRoot = component + + Object.keys(instanceOptions.components || {}).forEach(c => { + Constructor.component(c, instanceOptions.components[c]) }) if (options.slots) { diff --git a/packages/create-instance/extend-extended-components.js b/packages/create-instance/extend-extended-components.js new file mode 100644 index 000000000..1de7b5a39 --- /dev/null +++ b/packages/create-instance/extend-extended-components.js @@ -0,0 +1,62 @@ +import { warn } from 'shared/util' + +function createdFrom (extendOptions, componentOptions) { + while (extendOptions) { + if (extendOptions === componentOptions) { + return true + } + if (extendOptions._vueTestUtilsRoot === componentOptions) { + return true + } + extendOptions = extendOptions.extendOptions + } +} + +export function extendExtendedComponents ( + component, + _Vue, + logModifiedComponents, + excludedComponents = { } +) { + const componentOptions = typeof component === 'function' + ? component.extendOptions + : component + const extendedComponents = Object.create(null) + Object.keys(componentOptions.components || {}).forEach(c => { + if ( + componentOptions.components[c].extendOptions && + !excludedComponents[c] + ) { + if (logModifiedComponents) { + warn( + `an extended child component <${c}> has been modified ` + + `to ensure it has the correct instance properties. ` + + `This means it is not possible to find the component ` + + `with a component selector. To find the component, ` + + `you must stub it manually using the stubs mounting ` + + `option.` + ) + } + extendedComponents[c] = _Vue.extend( + componentOptions.components[c] + ) + } + extendExtendedComponents( + componentOptions.components[c], + _Vue, + logModifiedComponents + ) + }) + if (extendedComponents) { + _Vue.mixin({ + created () { + if (createdFrom(this.constructor, component)) { + Object.assign( + this.$options.components, + extendedComponents + ) + } + } + }) + } +} diff --git a/packages/create-instance/extract-instance-options.js b/packages/create-instance/extract-instance-options.js index ea6afad72..05756612c 100644 --- a/packages/create-instance/extract-instance-options.js +++ b/packages/create-instance/extract-instance-options.js @@ -10,13 +10,18 @@ const MOUNTING_OPTIONS = [ 'clone', 'attrs', 'listeners', - 'propsData' + 'propsData', + 'logModifiedComponents', + 'sync' ] export default function extractInstanceOptions ( options: Object ): Object { - const instanceOptions = { ...options } + const instanceOptions = { + ...options, + _vueTestUtilsRootExtendOptions: true + } MOUNTING_OPTIONS.forEach(mountingOption => { delete instanceOptions[mountingOption] }) diff --git a/packages/test-utils/src/shallow-mount.js b/packages/test-utils/src/shallow-mount.js index d07869074..4ea41445e 100644 --- a/packages/test-utils/src/shallow-mount.js +++ b/packages/test-utils/src/shallow-mount.js @@ -14,7 +14,7 @@ export default function shallowMount ( component: Component, options: Options = {} ): VueWrapper { - const vue = options.localVue || Vue + const _Vue = options.localVue || Vue // remove any recursive components added to the constructor // in vm._init from previous tests @@ -26,7 +26,7 @@ export default function shallowMount ( return mount(component, { ...options, components: { - ...createComponentStubsForGlobals(vue), + ...createComponentStubsForGlobals(_Vue), ...createComponentStubsForAll(component) } }) diff --git a/test/specs/mounting-options/localVue.spec.js b/test/specs/mounting-options/localVue.spec.js index a64011d1a..d7d8c5e4e 100644 --- a/test/specs/mounting-options/localVue.spec.js +++ b/test/specs/mounting-options/localVue.spec.js @@ -61,7 +61,7 @@ describeWithMountingMethods('options.localVue', mountingMethod => { } }) - it('is applies to child extended components', () => { + it('is applied to child extended components', () => { const ChildComponent = Vue.extend({ template: '
{{$route.params}}
' }) @@ -71,6 +71,7 @@ describeWithMountingMethods('options.localVue', mountingMethod => { }) const localVue = createLocalVue() localVue.prototype.$route = {} + mountingMethod(TestComponent, { localVue }) diff --git a/test/specs/mounting-options/slots.spec.js b/test/specs/mounting-options/slots.spec.js index 19e338b05..87511334b 100644 --- a/test/specs/mounting-options/slots.spec.js +++ b/test/specs/mounting-options/slots.spec.js @@ -4,6 +4,7 @@ import ComponentWithSlots from '~resources/components/component-with-slots.vue' import ComponentAsAClass from '~resources/components/component-as-a-class.vue' import { describeWithMountingMethods, vueVersion } from '~resources/utils' import { itSkipIf, itDoNotRunIf } from 'conditional-specs' +import { createLocalVue } from '~vue/test-utils' describeWithMountingMethods('options.slots', mountingMethod => { it('mounts component with default slot if passed component in slot object', () => { @@ -24,19 +25,16 @@ describeWithMountingMethods('options.slots', mountingMethod => { const CustomComponent = { render: h => h('time') } + const localVue = createLocalVue() + localVue.component('custom-component', CustomComponent) const TestComponent = { - template: '
', - components: { - 'custom-component': CustomComponent - } + template: '
' } const wrapper = mountingMethod(TestComponent, { slots: { default: '' }, - components: { - 'custom-component': CustomComponent - } + localVue }) if (mountingMethod.name === 'renderToString') { expect(wrapper).contains('