diff --git a/docs/en/api/config.md b/docs/en/api/config.md index a84befb06..9a85de8d1 100644 --- a/docs/en/api/config.md +++ b/docs/en/api/config.md @@ -79,3 +79,18 @@ VueTestUtils.config.provide['$logger'] = { } } ``` + +### `logModifiedComponents` + +- type: `Boolean` +- default: `true` + +Logs warning when extended child components are automatically stubbed. Hides warnings when set to `false`. Unlike other config options, this cannot be set on the mounting options. + +Example: + +```js +import VueTestUtils from '@vue/test-utils' + +VueTestUtils.config.logModifiedComponents = false +``` diff --git a/flow/options.flow.js b/flow/options.flow.js index b0ae8e99f..bff923149 100644 --- a/flow/options.flow.js +++ b/flow/options.flow.js @@ -9,5 +9,6 @@ declare type Options = { // eslint-disable-line no-undef stubs?: Object, context?: Object, attrs?: Object, - listeners?: Object + listeners?: Object, + logModifiedComponents?: Boolean } diff --git a/packages/create-instance/compile-template.js b/packages/create-instance/compile-template.js deleted file mode 100644 index b449bffbc..000000000 --- a/packages/create-instance/compile-template.js +++ /dev/null @@ -1,20 +0,0 @@ -// @flow - -import { compileToFunctions } from 'vue-template-compiler' - -export function compileTemplate (component: Component) { - if (component.components) { - Object.keys(component.components).forEach((c) => { - const cmp = component.components[c] - if (!cmp.render) { - compileTemplate(cmp) - } - }) - } - if (component.extends) { - compileTemplate(component.extends) - } - if (component.template) { - Object.assign(component, compileToFunctions(component.template)) - } -} diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 3eff2ceff..83631d7e9 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -9,8 +9,8 @@ import addListeners from './add-listeners' import addProvide from './add-provide' import { addEventLogger } from './log-events' import { createComponentStubs } from 'shared/stub-components' -import { throwError } from 'shared/util' -import { compileTemplate } from './compile-template' +import { throwError, warn } from 'shared/util' +import { compileTemplate } from 'shared/compile-template' import deleteoptions from './delete-mounting-options' import createFunctionalComponent from './create-functional-component' import { componentNeedsCompiling } from 'shared/validators' @@ -70,6 +70,16 @@ export default function createInstance ( } } + Object.keys(component.components || {}).forEach((c) => { + if (component.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 mocks mounting option.`) + } + instanceOptions.components[c] = vue.extend(component.components[c]) + } + }) + Object.keys(stubComponents).forEach(c => { vue.component(c, stubComponents[c]) }) diff --git a/packages/server-test-utils/types/index.d.ts b/packages/server-test-utils/types/index.d.ts index 26bf401ae..fcd7a9d12 100644 --- a/packages/server-test-utils/types/index.d.ts +++ b/packages/server-test-utils/types/index.d.ts @@ -49,7 +49,8 @@ interface VueTestUtilsConfigOptions { stubs?: Stubs mocks?: object methods?: Record - provide?: object + provide?: object, + logModifiedComponents?: Boolean } export declare let config: VueTestUtilsConfigOptions diff --git a/packages/shared/compile-template.js b/packages/shared/compile-template.js index b449bffbc..0aba6297d 100644 --- a/packages/shared/compile-template.js +++ b/packages/shared/compile-template.js @@ -11,9 +11,15 @@ export function compileTemplate (component: Component) { } }) } + if (component.extends) { compileTemplate(component.extends) } + + if (component.extendOptions && !component.options.render) { + compileTemplate(component.options) + } + if (component.template) { Object.assign(component, compileToFunctions(component.template)) } diff --git a/packages/shared/merge-options.js b/packages/shared/merge-options.js index e09ef95d7..d4ec612f3 100644 --- a/packages/shared/merge-options.js +++ b/packages/shared/merge-options.js @@ -26,6 +26,7 @@ export function mergeOptions ( ): Options { return { ...options, + logModifiedComponents: config.logModifiedComponents, stubs: getOptions('stubs', options.stubs, config), mocks: getOptions('mocks', options.mocks, config), methods: getOptions('methods', options.methods, config), diff --git a/packages/test-utils/src/config.js b/packages/test-utils/src/config.js index af5cb548c..4271c8ec6 100644 --- a/packages/test-utils/src/config.js +++ b/packages/test-utils/src/config.js @@ -8,5 +8,6 @@ export default { }, mocks: {}, methods: {}, - provide: {} + provide: {}, + logModifiedComponents: true } diff --git a/packages/test-utils/src/index.js b/packages/test-utils/src/index.js index b95abbce9..2c1982aa9 100644 --- a/packages/test-utils/src/index.js +++ b/packages/test-utils/src/index.js @@ -8,7 +8,7 @@ import config from './config' import { warn } from 'shared/util' function shallow (component, options) { - warn('shallow has been renamed to shallowMount and will be deprecated in 1.0.0') + warn('shallow has been renamed to shallowMount. shallow will be removed in 1.0.0, use shallowMount instead') return shallowMount(component, options) } diff --git a/packages/test-utils/types/index.d.ts b/packages/test-utils/types/index.d.ts index c0c0100df..ed8d0c502 100644 --- a/packages/test-utils/types/index.d.ts +++ b/packages/test-utils/types/index.d.ts @@ -139,7 +139,8 @@ interface VueTestUtilsConfigOptions { stubs?: Stubs mocks?: object methods?: Record - provide?: object + provide?: object, + logModifiedComponents?: Boolean } export declare function createLocalVue (): typeof Vue diff --git a/test/resources/utils.js b/test/resources/utils.js index 249ca9ee5..9d28cd6a0 100644 --- a/test/resources/utils.js +++ b/test/resources/utils.js @@ -1,7 +1,7 @@ /* global describe, it*/ import Vue from 'vue' -import { shallow, mount } from '~vue/test-utils' +import { shallowMount, mount } from '~vue/test-utils' import { renderToString } from '~vue/server-test-utils' export const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`) @@ -28,11 +28,10 @@ export const scopedSlotsSupported = vueVersion > 2 const shallowAndMount = process.env.TEST_ENV === 'node' ? [] - : [mount, shallow] -console.log(shallowAndMount) + : [mount, shallowMount] const shallowMountAndRender = process.env.TEST_ENV === 'node' ? [renderToString] - : [mount, shallow] + : [mount, shallowMount] export function describeWithShallowAndMount (spec, cb) { if (shallowAndMount.length > 0) { diff --git a/test/specs/components/TransitionStub.spec.js b/test/specs/components/TransitionStub.spec.js index 602c8207d..829bac1f8 100644 --- a/test/specs/components/TransitionStub.spec.js +++ b/test/specs/components/TransitionStub.spec.js @@ -3,8 +3,17 @@ import { describeWithShallowAndMount } from '~resources/utils' import { TransitionStub } from '~vue/test-utils' describeWithShallowAndMount('TransitionStub', (mountingMethod) => { + let consoleError + + beforeEach(() => { + consoleError = sinon.stub(console, 'error') + }) + + afterEach(() => { + consoleError.restore() + }) + it('update synchronously when used as stubs for Transition', () => { - console.log(TransitionStub) const wrapper = mountingMethod(ComponentWithTransition, { stubs: { 'transition': TransitionStub @@ -48,14 +57,12 @@ describeWithShallowAndMount('TransitionStub', (mountingMethod) => { ` } const msg = '[vue-test-utils]: can only be used on a single element. Use for lists.' - const error = sinon.stub(console, 'error') mountingMethod(TestComponent, { stubs: { 'transition': TransitionStub } }) - expect(error).calledWith(msg) - error.restore() + expect(consoleError).calledWith(msg) }) it('handles keyed transitions', () => { diff --git a/test/specs/config.spec.js b/test/specs/config.spec.js index 0a5893725..2fd5717ab 100644 --- a/test/specs/config.spec.js +++ b/test/specs/config.spec.js @@ -1,21 +1,34 @@ -import { describeWithShallowAndMount, itDoNotRunIf } from '~resources/utils' +import { + describeWithShallowAndMount, + itDoNotRunIf, + itSkipIf, + vueVersion +} from '~resources/utils' import { config, TransitionStub, TransitionGroupStub, createLocalVue } from '~vue/test-utils' +import Vue from 'vue' describeWithShallowAndMount('config', (mountingMethod) => { let configStubsSave + let consoleError + let configLogSave + beforeEach(() => { TransitionGroupStub.name = 'another-temp-name' TransitionStub.name = 'a-temp-name' configStubsSave = config.stubs + configLogSave = config.logModifiedComponents + consoleError = sinon.stub(console, 'error') }) afterEach(() => { TransitionGroupStub.name = 'transition-group' TransitionStub.name = 'transition' config.stubs = configStubsSave + config.logModifiedComponents = configLogSave + consoleError.restore() }) - itDoNotRunIf(mountingMethod.name === 'shallow', + itDoNotRunIf(mountingMethod.name === 'shallowMount', 'stubs transition and transition-group by default', () => { const testComponent = { template: ` @@ -121,4 +134,21 @@ describeWithShallowAndMount('config', (mountingMethod) => { expect(wrapper.contains(TransitionGroupStub)).to.equal(false) expect(wrapper.contains(TransitionStub)).to.equal(false) }) + + itSkipIf( + vueVersion < 2.3, + 'does not log when component is extended if logModifiedComponents is false', () => { + const ChildComponent = Vue.extend({ + template: '' + }) + const TestComponent = { + template: '', + components: { + ChildComponent + } + } + config.logModifiedComponents = false + mountingMethod(TestComponent) + expect(consoleError.called).to.equal(false) + }) }) diff --git a/test/specs/create-local-vue.spec.js b/test/specs/create-local-vue.spec.js index 292f3f73f..0be21cb4c 100644 --- a/test/specs/create-local-vue.spec.js +++ b/test/specs/create-local-vue.spec.js @@ -70,7 +70,7 @@ describeWithShallowAndMount('createLocalVue', (mountingMethod) => { }) itDoNotRunIf( - mountingMethod.name === 'shallow', + mountingMethod.name === 'shallowMount', 'Router should work properly with local Vue', () => { const localVue = createLocalVue() localVue.use(VueRouter) diff --git a/test/specs/mount.spec.js b/test/specs/mount.spec.js index 24f40730d..d3bdec784 100644 --- a/test/specs/mount.spec.js +++ b/test/specs/mount.spec.js @@ -8,6 +8,16 @@ import { injectSupported, vueVersion, describeIf } from '~resources/utils' describeIf(process.env.TEST_ENV !== 'node', 'mount', () => { + let consoleError + + beforeEach(() => { + consoleError = sinon.stub(console, 'error') + }) + + afterEach(() => { + consoleError.restore() + }) + it('returns new VueWrapper with mounted Vue instance if no options are passed', () => { const compiled = compileToFunctions('
') const wrapper = mount(compiled) @@ -120,6 +130,21 @@ describeIf(process.env.TEST_ENV !== 'node', expect(wrapper.html()).to.equal(`
foo
`) }) + it('logs if component is extended', () => { + const msg = '[vue-test-utils]: an extended child component ChildComponent 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 mocks mounting option.' + const ChildComponent = Vue.extend({ + template: '' + }) + const TestComponent = { + template: '', + components: { + ChildComponent + } + } + mount(TestComponent) + expect(consoleError).calledWith(msg) + }) + it('deletes mounting options before passing options to component', () => { const wrapper = mount({ render: h => h('div') diff --git a/test/specs/mounting-options/localVue.spec.js b/test/specs/mounting-options/localVue.spec.js index bfce95116..8eeed35d9 100644 --- a/test/specs/mounting-options/localVue.spec.js +++ b/test/specs/mounting-options/localVue.spec.js @@ -2,8 +2,11 @@ import Vue from 'vue' import { describeWithMountingMethods, itSkipIf, - isRunningPhantomJS + isRunningPhantomJS, + vueVersion } from '~resources/utils' +import { createLocalVue } from '~vue/test-utils' +import Vuex from 'vuex' describeWithMountingMethods('options.localVue', (mountingMethod) => { itSkipIf( @@ -30,4 +33,40 @@ describeWithMountingMethods('options.localVue', (mountingMethod) => { : freshWrapper.html() expect(freshHTML).to.not.contain('some value') }) + + itSkipIf( + vueVersion < 2.3, + 'works correctly with extended children', () => { + const localVue = createLocalVue() + localVue.use(Vuex) + const store = new Vuex.Store({ + state: { val: 2 } + }) + const ChildComponent = Vue.extend({ + template: '{{val}}', + computed: { + val () { + return this.$store.state.val + } + } + }) + const TestComponent = { + template: '
', + components: { + ChildComponent + } + } + const wrapper = mountingMethod(TestComponent, { + localVue, + store + }) + const HTML = mountingMethod.name === 'renderToString' + ? wrapper + : wrapper.html() + if (mountingMethod.name === 'shallowMount') { + expect(HTML).to.not.contain('2') + } else { + expect(HTML).to.contain('2') + } + }) }) diff --git a/test/specs/mounting-options/methods.spec.js b/test/specs/mounting-options/methods.spec.js index b38410a4b..8ee1306c8 100644 --- a/test/specs/mounting-options/methods.spec.js +++ b/test/specs/mounting-options/methods.spec.js @@ -23,7 +23,6 @@ describeWithMountingMethods('options.methods', (mountingMethod) => { const HTML = mountingMethod.name === 'renderToString' ? wrapper : wrapper.html() - console.log(wrapper) expect(HTML).to.contain('methodFromOptions') }) }) diff --git a/test/specs/mounting-options/mocks.spec.js b/test/specs/mounting-options/mocks.spec.js index 31fd1a90a..712f74df1 100644 --- a/test/specs/mounting-options/mocks.spec.js +++ b/test/specs/mounting-options/mocks.spec.js @@ -8,14 +8,17 @@ import { describeWithMountingMethods('options.mocks', (mountingMethod) => { let configMocksSave + let consoleError beforeEach(() => { configMocksSave = config.mocks config.mocks = {} + consoleError = sinon.stub(console, 'error') }) afterEach(() => { config.mocks = configMocksSave + consoleError.restore() }) it('adds variables to vm when passed', () => { @@ -71,7 +74,7 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => { expect(wrapper.text()).to.contain('changed value') }) - itDoNotRunIf(mountingMethod.name === 'shallow', + itDoNotRunIf(mountingMethod.name === 'shallowMount', 'adds variables available to nested vms', () => { const count = 1 const wrapper = mountingMethod({ @@ -88,7 +91,7 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => { expect(HTML).contains(count) }) - itDoNotRunIf(mountingMethod.name === 'shallow', + itDoNotRunIf(mountingMethod.name === 'shallowMount', 'adds variables available to nested vms using localVue', () => { const localVue = createLocalVue() const count = 1 @@ -121,7 +124,6 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => { }) it('logs that a property cannot be overwritten if there are problems writing', () => { - const error = sinon.stub(console, 'error') const localVue = createLocalVue() Object.defineProperty(localVue.prototype, '$store', { value: 42 @@ -134,8 +136,7 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => { } }) const msg = '[vue-test-utils]: could not overwrite property $store, this usually caused by a plugin that has added the property as a read-only value' - expect(error.calledWith(msg)).to.equal(true) - error.restore() + expect(consoleError.calledWith(msg)).to.equal(true) }) it('prioritize mounting options over config', () => { @@ -155,12 +156,10 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => { const HTML = mountingMethod.name === 'renderToString' ? wrapper : wrapper.html() - console.log(wrapper) expect(HTML).to.contain('locallyMockedValue') }) it('logs that a property cannot be overwritten if there are problems writing', () => { - const error = sinon.stub(console, 'error') const localVue = createLocalVue() Object.defineProperty(localVue.prototype, '$val', { value: 42 @@ -173,7 +172,6 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => { } }) const msg = '[vue-test-utils]: could not overwrite property $val, this usually caused by a plugin that has added the property as a read-only value' - expect(error.calledWith(msg)).to.equal(true) - error.restore() + expect(consoleError.calledWith(msg)).to.equal(true) }) }) diff --git a/test/specs/mounting-options/stubs.spec.js b/test/specs/mounting-options/stubs.spec.js index 94bcd1d19..3122fb5de 100644 --- a/test/specs/mounting-options/stubs.spec.js +++ b/test/specs/mounting-options/stubs.spec.js @@ -84,7 +84,7 @@ describeWithMountingMethods('options.stub', (mountingMethod) => { }) }) - itDoNotRunIf(mountingMethod.name === 'shallow' || + itDoNotRunIf(mountingMethod.name === 'shallowMount' || mountingMethod.name === 'renderToString', 'does not modify component directly', () => { const wrapper = mountingMethod(ComponentWithNestedChildren, { @@ -178,7 +178,7 @@ describeWithMountingMethods('options.stub', (mountingMethod) => { require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = compilerSave }) - itDoNotRunIf(mountingMethod.name === 'shallow', + itDoNotRunIf(mountingMethod.name === 'shallowMount', 'does not stub component when set to false', () => { const wrapper = mountingMethod(ComponentWithChild, { stubs: { @@ -249,7 +249,7 @@ describeWithMountingMethods('options.stub', (mountingMethod) => { }) itDoNotRunIf( - mountingMethod.name === 'shallow' || + mountingMethod.name === 'shallowMount' || mountingMethod.name === 'renderToString', 'stubs on child components', () => { const TestComponent = { diff --git a/test/specs/shallow-mount.spec.js b/test/specs/shallow-mount.spec.js index 80f4310d7..b9fa51d43 100644 --- a/test/specs/shallow-mount.spec.js +++ b/test/specs/shallow-mount.spec.js @@ -11,7 +11,7 @@ import RecursiveComponent from '~resources/components/recursive-component.vue' import { vueVersion, describeIf } from '~resources/utils' describeIf(process.env.TEST_ENV !== 'node', - 'shallow', () => { + 'shallowMount', () => { let info beforeEach(() => { diff --git a/test/specs/shallow.spec.js b/test/specs/shallow.spec.js index 441c25fa8..b2fc0abce 100644 --- a/test/specs/shallow.spec.js +++ b/test/specs/shallow.spec.js @@ -11,7 +11,7 @@ import RecursiveComponent from '~resources/components/recursive-component.vue' import { vueVersion, describeIf } from '~resources/utils' describeIf(process.env.TEST_ENV !== 'node', - 'shallow', () => { + 'shallowMount', () => { let info beforeEach(() => { diff --git a/test/specs/wrapper/html.spec.js b/test/specs/wrapper/html.spec.js index 412068557..71d241fda 100644 --- a/test/specs/wrapper/html.spec.js +++ b/test/specs/wrapper/html.spec.js @@ -11,7 +11,7 @@ describeWithShallowAndMount('html', (mountingMethod) => { }) it('returns a VueWrappers HTML as a string when component has no render function', () => { - if (mountingMethod.name === 'shallow') return + if (mountingMethod.name === 'shallowMount') return const wrapper = mountingMethod({ template: `
1
`, components: {