diff --git a/packages/create-instance/add-mocks.js b/packages/create-instance/add-mocks.js index b509f0495..9b5a6cfee 100644 --- a/packages/create-instance/add-mocks.js +++ b/packages/create-instance/add-mocks.js @@ -3,11 +3,15 @@ import $$Vue from 'vue' import { warn } from 'shared/util' export default function addMocks ( - mockedProperties: Object = {}, + mockedProperties: Object | false = {}, Vue: Component ): void { + if (mockedProperties === false) { + return + } Object.keys(mockedProperties).forEach(key => { try { + // $FlowIgnore Vue.prototype[key] = mockedProperties[key] } catch (e) { warn( @@ -16,6 +20,7 @@ export default function addMocks ( `the property as a read-only value` ) } + // $FlowIgnore $$Vue.util.defineReactive(Vue, key, mockedProperties[key]) }) } diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 52d7c361f..1981dbe5c 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -25,6 +25,22 @@ function compileTemplateForSlots (slots: Object): void { }) } +function vueExtendUnsupportedOption (option: string) { + return `options.${option} is not supported for ` + + `components created with Vue.extend in Vue < 2.3. ` + + `You can set ${option} to false to mount the component.` +} + +// these options aren't supported if Vue is version < 2.3 +// for components using Vue.extend. This is due to a bug +// that means the mixins we use to add properties are not applied +// correctly +const UNSUPPORTED_VERSION_OPTIONS = [ + 'mocks', + 'stubs', + 'localVue' +] + export default function createInstance ( component: Component, options: Options, @@ -34,6 +50,18 @@ export default function createInstance ( // Remove cached constructor delete component._Ctor + if ( + vueVersion < 2.3 && + typeof component === 'function' && + component.options + ) { + UNSUPPORTED_VERSION_OPTIONS.forEach((option) => { + if (options[option]) { + throwError(vueExtendUnsupportedOption(option)) + } + }) + } + // instance options are options that are passed to the // root instance when it's instantiated const instanceOptions = extractInstanceOptions(options) diff --git a/packages/shared/merge-options.js b/packages/shared/merge-options.js index 1adb4c3f9..7c4b71b92 100644 --- a/packages/shared/merge-options.js +++ b/packages/shared/merge-options.js @@ -2,6 +2,9 @@ import { normalizeStubs } from './normalize' function getOption (option, config?: Object): any { + if (option === false) { + return false + } if (option || (config && Object.keys(config).length > 0)) { if (option instanceof Function) { return option diff --git a/packages/shared/normalize.js b/packages/shared/normalize.js index 5c359ed9d..1791cff97 100644 --- a/packages/shared/normalize.js +++ b/packages/shared/normalize.js @@ -2,6 +2,9 @@ import { isPlainObject } from './validators' import { throwError } from './util' export function normalizeStubs (stubs = {}) { + if (stubs === false) { + return false + } if (isPlainObject(stubs)) { return stubs } diff --git a/packages/test-utils/src/shallow-mount.js b/packages/test-utils/src/shallow-mount.js index c78fb832f..16662038b 100644 --- a/packages/test-utils/src/shallow-mount.js +++ b/packages/test-utils/src/shallow-mount.js @@ -30,7 +30,7 @@ export default function shallowMount ( // In Vue.extends, Vue adds a recursive component to the options // This stub will override the component added by Vue // $FlowIgnore - if (!options.stubs[component.name]) { + if (options.stubs && !options.stubs[component.name]) { // $FlowIgnore options.stubs[component.name] = createBlankStub(component, component.name) } diff --git a/packages/test-utils/types/index.d.ts b/packages/test-utils/types/index.d.ts index 32e9445a3..e521aa15a 100644 --- a/packages/test-utils/types/index.d.ts +++ b/packages/test-utils/types/index.d.ts @@ -125,11 +125,11 @@ interface MountOptions extends ComponentOptions { attachToDocument?: boolean context?: VNodeData localVue?: typeof Vue - mocks?: object + mocks?: object | false parentComponent?: Component slots?: Slots scopedSlots?: Record - stubs?: Stubs, + stubs?: Stubs | false, attrs?: Record listeners?: Record sync?: boolean diff --git a/test/specs/mount.spec.js b/test/specs/mount.spec.js index db4fa2e53..e5ab2c6cd 100644 --- a/test/specs/mount.spec.js +++ b/test/specs/mount.spec.js @@ -135,24 +135,26 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => { expect(wrapper.html()).to.equal(`
foo
`) }) - it('overrides methods', () => { - const stub = sinon.stub() - const TestComponent = Vue.extend({ - template: '
', - methods: { - callStub () { - stub() + itDoNotRunIf( + vueVersion < 2.3, + 'overrides methods', () => { + const stub = sinon.stub() + const TestComponent = Vue.extend({ + template: '
', + methods: { + callStub () { + stub() + } } - } - }) - mount(TestComponent, { - methods: { - callStub () {} - } - }).vm.callStub() + }) + mount(TestComponent, { + methods: { + callStub () {} + } + }).vm.callStub() - expect(stub).not.called - }) + expect(stub).not.called + }) it.skip('overrides component prototype', () => { const mountSpy = sinon.spy() @@ -268,22 +270,24 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => { expect(wrapper.vm.$options.listeners).to.equal(undefined) }) - it('injects store correctly', () => { - const localVue = createLocalVue() - localVue.use(Vuex) - const store = new Vuex.Store() - const wrapper = mount(ComponentAsAClass, { - store, - localVue + itDoNotRunIf( + vueVersion < 2.3, + 'injects store correctly', () => { + const localVue = createLocalVue() + localVue.use(Vuex) + const store = new Vuex.Store() + const wrapper = mount(ComponentAsAClass, { + store, + localVue + }) + wrapper.vm.getters + mount( + { + template: '
{{$store.getters}}
' + }, + { store, localVue } + ) }) - wrapper.vm.getters - mount( - { - template: '
{{$store.getters}}
' - }, - { store, localVue } - ) - }) it('propagates errors when they are thrown', () => { const TestComponent = { diff --git a/test/specs/mounting-options/context.spec.js b/test/specs/mounting-options/context.spec.js index 134cbef75..e1888e7e3 100644 --- a/test/specs/mounting-options/context.spec.js +++ b/test/specs/mounting-options/context.spec.js @@ -45,7 +45,7 @@ describeWithMountingMethods('options.context', mountingMethod => { render: h => h('div') }) const context = {} - const fn = () => mountingMethod(Component, { context }) + const fn = () => mountingMethod(Component, { context, stubs: false, mocks: false }) expect(fn).not.to.throw() }) diff --git a/test/specs/mounting-options/localVue.spec.js b/test/specs/mounting-options/localVue.spec.js index ab6146d42..ec20d6d88 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 } from 'conditional-specs' +import { itSkipIf, itRunIf } from 'conditional-specs' import Vuex from 'vuex' describeWithMountingMethods('options.localVue', mountingMethod => { @@ -154,4 +154,24 @@ describeWithMountingMethods('options.localVue', mountingMethod => { localVue }) }) + + itRunIf( + vueVersion < 2.3, + 'throws an error if used with an extended component in Vue 2.3', () => { + const TestComponent = Vue.extend({ + template: '
' + }) + const message = + `[vue-test-utils]: options.localVue is not supported for components ` + + `created with Vue.extend in Vue < 2.3. You can set localVue to false ` + + `to mount the component.` + + const fn = () => mountingMethod(TestComponent, { + localVue: createLocalVue(), + stubs: false, + mocks: false + }) + expect(fn).to.throw() + .with.property('message', message) + }) }) diff --git a/test/specs/mounting-options/mocks.spec.js b/test/specs/mounting-options/mocks.spec.js index 0dc06713f..15564c3fc 100644 --- a/test/specs/mounting-options/mocks.spec.js +++ b/test/specs/mounting-options/mocks.spec.js @@ -3,7 +3,7 @@ import Vue from 'vue' import Component from '~resources/components/component.vue' import ComponentWithVuex from '~resources/components/component-with-vuex.vue' import { describeWithMountingMethods, vueVersion } from '~resources/utils' -import { itDoNotRunIf, itSkipIf } from 'conditional-specs' +import { itDoNotRunIf, itSkipIf, itRunIf } from 'conditional-specs' describeWithMountingMethods('options.mocks', mountingMethod => { let configMocksSave @@ -200,4 +200,22 @@ describeWithMountingMethods('options.mocks', mountingMethod => { mountingMethod.name === 'renderToString' ? wrapper : wrapper.html() expect(HTML).to.contain('locallyMockedValue') }) + + itRunIf( + vueVersion < 2.3, + 'throws an error if used with an extended component in Vue 2.3', () => { + const TestComponent = Vue.extend({ + template: '
' + }) + const message = + `[vue-test-utils]: options.mocks is not supported for components ` + + `created with Vue.extend in Vue < 2.3. You can set mocks to false ` + + `to mount the component.` + const fn = () => mountingMethod(TestComponent, { + mocks: { something: 'true' }, + stubs: false + }) + expect(fn).to.throw() + .with.property('message', message) + }) }) diff --git a/test/specs/mounting-options/stubs.spec.js b/test/specs/mounting-options/stubs.spec.js index 812f46616..c2e2c2dc6 100644 --- a/test/specs/mounting-options/stubs.spec.js +++ b/test/specs/mounting-options/stubs.spec.js @@ -6,7 +6,7 @@ import { createLocalVue, config } from '~vue/test-utils' import { config as serverConfig } from '~vue/server-test-utils' import Vue from 'vue' import { describeWithMountingMethods, vueVersion } from '~resources/utils' -import { itDoNotRunIf, itSkipIf } from 'conditional-specs' +import { itDoNotRunIf, itSkipIf, itRunIf } from 'conditional-specs' describeWithMountingMethods('options.stub', mountingMethod => { let info @@ -512,4 +512,24 @@ describeWithMountingMethods('options.stub', mountingMethod => { .with.property('message', error) }) }) + + itRunIf( + vueVersion < 2.3, + 'throws an error if used with an extended component in Vue 2.3', () => { + const TestComponent = Vue.extend({ + template: '
' + }) + const message = + `[vue-test-utils]: options.stubs is not supported for components ` + + `created with Vue.extend in Vue < 2.3. You can set stubs to false ` + + `to mount the component.` + const fn = () => mountingMethod(TestComponent, { + stubs: { + something: 'true' + }, + mocks: false + }) + expect(fn).to.throw() + .with.property('message', message) + }) }) diff --git a/test/specs/wrapper/is.spec.js b/test/specs/wrapper/is.spec.js index 606b2f32b..5ddb92060 100644 --- a/test/specs/wrapper/is.spec.js +++ b/test/specs/wrapper/is.spec.js @@ -67,7 +67,10 @@ describeWithShallowAndMount('is', mountingMethod => { }) it('returns true if root node matches Component extending class component', () => { - const wrapper = mountingMethod(ComponentAsAClass) + const wrapper = mountingMethod(ComponentAsAClass, { + mocks: false, + stubs: false + }) expect(wrapper.is(ComponentAsAClass)).to.equal(true) })