Skip to content

Commit

Permalink
fix: throw error if unsupported options passed in vue < 2.3 (#910)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyerburgh authored Aug 9, 2018
1 parent 807332a commit e8d9547
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 40 deletions.
7 changes: 6 additions & 1 deletion packages/create-instance/add-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -16,6 +20,7 @@ export default function addMocks (
`the property as a read-only value`
)
}
// $FlowIgnore
$$Vue.util.defineReactive(Vue, key, mockedProperties[key])
})
}
28 changes: 28 additions & 0 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/merge-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions packages/shared/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion packages/test-utils/src/shallow-mount.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/test-utils/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ interface MountOptions<V extends Vue> extends ComponentOptions<V> {
attachToDocument?: boolean
context?: VNodeData
localVue?: typeof Vue
mocks?: object
mocks?: object | false
parentComponent?: Component
slots?: Slots
scopedSlots?: Record<string, string>
stubs?: Stubs,
stubs?: Stubs | false,
attrs?: Record<string, string>
listeners?: Record<string, Function | Function[]>
sync?: boolean
Expand Down
66 changes: 35 additions & 31 deletions test/specs/mount.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,24 +135,26 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
expect(wrapper.html()).to.equal(`<div>foo</div>`)
})

it('overrides methods', () => {
const stub = sinon.stub()
const TestComponent = Vue.extend({
template: '<div />',
methods: {
callStub () {
stub()
itDoNotRunIf(
vueVersion < 2.3,
'overrides methods', () => {
const stub = sinon.stub()
const TestComponent = Vue.extend({
template: '<div />',
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()
Expand Down Expand Up @@ -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: '<div>{{$store.getters}}</div>'
},
{ store, localVue }
)
})
wrapper.vm.getters
mount(
{
template: '<div>{{$store.getters}}</div>'
},
{ store, localVue }
)
})

it('propagates errors when they are thrown', () => {
const TestComponent = {
Expand Down
2 changes: 1 addition & 1 deletion test/specs/mounting-options/context.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})

Expand Down
22 changes: 21 additions & 1 deletion test/specs/mounting-options/localVue.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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: '<div></div>'
})
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)
})
})
20 changes: 19 additions & 1 deletion test/specs/mounting-options/mocks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: '<div></div>'
})
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)
})
})
22 changes: 21 additions & 1 deletion test/specs/mounting-options/stubs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: '<div></div>'
})
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)
})
})
5 changes: 4 additions & 1 deletion test/specs/wrapper/is.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand Down

0 comments on commit e8d9547

Please sign in to comment.