Skip to content

Commit e8d9547

Browse files
authored
fix: throw error if unsupported options passed in vue < 2.3 (vuejs#910)
1 parent 807332a commit e8d9547

File tree

12 files changed

+144
-40
lines changed

12 files changed

+144
-40
lines changed

packages/create-instance/add-mocks.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import $$Vue from 'vue'
33
import { warn } from 'shared/util'
44

55
export default function addMocks (
6-
mockedProperties: Object = {},
6+
mockedProperties: Object | false = {},
77
Vue: Component
88
): void {
9+
if (mockedProperties === false) {
10+
return
11+
}
912
Object.keys(mockedProperties).forEach(key => {
1013
try {
14+
// $FlowIgnore
1115
Vue.prototype[key] = mockedProperties[key]
1216
} catch (e) {
1317
warn(
@@ -16,6 +20,7 @@ export default function addMocks (
1620
`the property as a read-only value`
1721
)
1822
}
23+
// $FlowIgnore
1924
$$Vue.util.defineReactive(Vue, key, mockedProperties[key])
2025
})
2126
}

packages/create-instance/create-instance.js

+28
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ function compileTemplateForSlots (slots: Object): void {
2525
})
2626
}
2727

28+
function vueExtendUnsupportedOption (option: string) {
29+
return `options.${option} is not supported for ` +
30+
`components created with Vue.extend in Vue < 2.3. ` +
31+
`You can set ${option} to false to mount the component.`
32+
}
33+
34+
// these options aren't supported if Vue is version < 2.3
35+
// for components using Vue.extend. This is due to a bug
36+
// that means the mixins we use to add properties are not applied
37+
// correctly
38+
const UNSUPPORTED_VERSION_OPTIONS = [
39+
'mocks',
40+
'stubs',
41+
'localVue'
42+
]
43+
2844
export default function createInstance (
2945
component: Component,
3046
options: Options,
@@ -34,6 +50,18 @@ export default function createInstance (
3450
// Remove cached constructor
3551
delete component._Ctor
3652

53+
if (
54+
vueVersion < 2.3 &&
55+
typeof component === 'function' &&
56+
component.options
57+
) {
58+
UNSUPPORTED_VERSION_OPTIONS.forEach((option) => {
59+
if (options[option]) {
60+
throwError(vueExtendUnsupportedOption(option))
61+
}
62+
})
63+
}
64+
3765
// instance options are options that are passed to the
3866
// root instance when it's instantiated
3967
const instanceOptions = extractInstanceOptions(options)

packages/shared/merge-options.js

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { normalizeStubs } from './normalize'
33

44
function getOption (option, config?: Object): any {
5+
if (option === false) {
6+
return false
7+
}
58
if (option || (config && Object.keys(config).length > 0)) {
69
if (option instanceof Function) {
710
return option

packages/shared/normalize.js

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { isPlainObject } from './validators'
22
import { throwError } from './util'
33

44
export function normalizeStubs (stubs = {}) {
5+
if (stubs === false) {
6+
return false
7+
}
58
if (isPlainObject(stubs)) {
69
return stubs
710
}

packages/test-utils/src/shallow-mount.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default function shallowMount (
3030
// In Vue.extends, Vue adds a recursive component to the options
3131
// This stub will override the component added by Vue
3232
// $FlowIgnore
33-
if (!options.stubs[component.name]) {
33+
if (options.stubs && !options.stubs[component.name]) {
3434
// $FlowIgnore
3535
options.stubs[component.name] = createBlankStub(component, component.name)
3636
}

packages/test-utils/types/index.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,11 @@ interface MountOptions<V extends Vue> extends ComponentOptions<V> {
125125
attachToDocument?: boolean
126126
context?: VNodeData
127127
localVue?: typeof Vue
128-
mocks?: object
128+
mocks?: object | false
129129
parentComponent?: Component
130130
slots?: Slots
131131
scopedSlots?: Record<string, string>
132-
stubs?: Stubs,
132+
stubs?: Stubs | false,
133133
attrs?: Record<string, string>
134134
listeners?: Record<string, Function | Function[]>
135135
sync?: boolean

test/specs/mount.spec.js

+35-31
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,26 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
135135
expect(wrapper.html()).to.equal(`<div>foo</div>`)
136136
})
137137

138-
it('overrides methods', () => {
139-
const stub = sinon.stub()
140-
const TestComponent = Vue.extend({
141-
template: '<div />',
142-
methods: {
143-
callStub () {
144-
stub()
138+
itDoNotRunIf(
139+
vueVersion < 2.3,
140+
'overrides methods', () => {
141+
const stub = sinon.stub()
142+
const TestComponent = Vue.extend({
143+
template: '<div />',
144+
methods: {
145+
callStub () {
146+
stub()
147+
}
145148
}
146-
}
147-
})
148-
mount(TestComponent, {
149-
methods: {
150-
callStub () {}
151-
}
152-
}).vm.callStub()
149+
})
150+
mount(TestComponent, {
151+
methods: {
152+
callStub () {}
153+
}
154+
}).vm.callStub()
153155

154-
expect(stub).not.called
155-
})
156+
expect(stub).not.called
157+
})
156158

157159
it.skip('overrides component prototype', () => {
158160
const mountSpy = sinon.spy()
@@ -268,22 +270,24 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'mount', () => {
268270
expect(wrapper.vm.$options.listeners).to.equal(undefined)
269271
})
270272

271-
it('injects store correctly', () => {
272-
const localVue = createLocalVue()
273-
localVue.use(Vuex)
274-
const store = new Vuex.Store()
275-
const wrapper = mount(ComponentAsAClass, {
276-
store,
277-
localVue
273+
itDoNotRunIf(
274+
vueVersion < 2.3,
275+
'injects store correctly', () => {
276+
const localVue = createLocalVue()
277+
localVue.use(Vuex)
278+
const store = new Vuex.Store()
279+
const wrapper = mount(ComponentAsAClass, {
280+
store,
281+
localVue
282+
})
283+
wrapper.vm.getters
284+
mount(
285+
{
286+
template: '<div>{{$store.getters}}</div>'
287+
},
288+
{ store, localVue }
289+
)
278290
})
279-
wrapper.vm.getters
280-
mount(
281-
{
282-
template: '<div>{{$store.getters}}</div>'
283-
},
284-
{ store, localVue }
285-
)
286-
})
287291

288292
it('propagates errors when they are thrown', () => {
289293
const TestComponent = {

test/specs/mounting-options/context.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describeWithMountingMethods('options.context', mountingMethod => {
4545
render: h => h('div')
4646
})
4747
const context = {}
48-
const fn = () => mountingMethod(Component, { context })
48+
const fn = () => mountingMethod(Component, { context, stubs: false, mocks: false })
4949
expect(fn).not.to.throw()
5050
})
5151

test/specs/mounting-options/localVue.spec.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
vueVersion
66
} from '~resources/utils'
77
import { createLocalVue } from '~vue/test-utils'
8-
import { itSkipIf } from 'conditional-specs'
8+
import { itSkipIf, itRunIf } from 'conditional-specs'
99
import Vuex from 'vuex'
1010

1111
describeWithMountingMethods('options.localVue', mountingMethod => {
@@ -154,4 +154,24 @@ describeWithMountingMethods('options.localVue', mountingMethod => {
154154
localVue
155155
})
156156
})
157+
158+
itRunIf(
159+
vueVersion < 2.3,
160+
'throws an error if used with an extended component in Vue 2.3', () => {
161+
const TestComponent = Vue.extend({
162+
template: '<div></div>'
163+
})
164+
const message =
165+
`[vue-test-utils]: options.localVue is not supported for components ` +
166+
`created with Vue.extend in Vue < 2.3. You can set localVue to false ` +
167+
`to mount the component.`
168+
169+
const fn = () => mountingMethod(TestComponent, {
170+
localVue: createLocalVue(),
171+
stubs: false,
172+
mocks: false
173+
})
174+
expect(fn).to.throw()
175+
.with.property('message', message)
176+
})
157177
})

test/specs/mounting-options/mocks.spec.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Vue from 'vue'
33
import Component from '~resources/components/component.vue'
44
import ComponentWithVuex from '~resources/components/component-with-vuex.vue'
55
import { describeWithMountingMethods, vueVersion } from '~resources/utils'
6-
import { itDoNotRunIf, itSkipIf } from 'conditional-specs'
6+
import { itDoNotRunIf, itSkipIf, itRunIf } from 'conditional-specs'
77

88
describeWithMountingMethods('options.mocks', mountingMethod => {
99
let configMocksSave
@@ -200,4 +200,22 @@ describeWithMountingMethods('options.mocks', mountingMethod => {
200200
mountingMethod.name === 'renderToString' ? wrapper : wrapper.html()
201201
expect(HTML).to.contain('locallyMockedValue')
202202
})
203+
204+
itRunIf(
205+
vueVersion < 2.3,
206+
'throws an error if used with an extended component in Vue 2.3', () => {
207+
const TestComponent = Vue.extend({
208+
template: '<div></div>'
209+
})
210+
const message =
211+
`[vue-test-utils]: options.mocks is not supported for components ` +
212+
`created with Vue.extend in Vue < 2.3. You can set mocks to false ` +
213+
`to mount the component.`
214+
const fn = () => mountingMethod(TestComponent, {
215+
mocks: { something: 'true' },
216+
stubs: false
217+
})
218+
expect(fn).to.throw()
219+
.with.property('message', message)
220+
})
203221
})

test/specs/mounting-options/stubs.spec.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createLocalVue, config } from '~vue/test-utils'
66
import { config as serverConfig } from '~vue/server-test-utils'
77
import Vue from 'vue'
88
import { describeWithMountingMethods, vueVersion } from '~resources/utils'
9-
import { itDoNotRunIf, itSkipIf } from 'conditional-specs'
9+
import { itDoNotRunIf, itSkipIf, itRunIf } from 'conditional-specs'
1010

1111
describeWithMountingMethods('options.stub', mountingMethod => {
1212
let info
@@ -512,4 +512,24 @@ describeWithMountingMethods('options.stub', mountingMethod => {
512512
.with.property('message', error)
513513
})
514514
})
515+
516+
itRunIf(
517+
vueVersion < 2.3,
518+
'throws an error if used with an extended component in Vue 2.3', () => {
519+
const TestComponent = Vue.extend({
520+
template: '<div></div>'
521+
})
522+
const message =
523+
`[vue-test-utils]: options.stubs is not supported for components ` +
524+
`created with Vue.extend in Vue < 2.3. You can set stubs to false ` +
525+
`to mount the component.`
526+
const fn = () => mountingMethod(TestComponent, {
527+
stubs: {
528+
something: 'true'
529+
},
530+
mocks: false
531+
})
532+
expect(fn).to.throw()
533+
.with.property('message', message)
534+
})
515535
})

test/specs/wrapper/is.spec.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ describeWithShallowAndMount('is', mountingMethod => {
6767
})
6868

6969
it('returns true if root node matches Component extending class component', () => {
70-
const wrapper = mountingMethod(ComponentAsAClass)
70+
const wrapper = mountingMethod(ComponentAsAClass, {
71+
mocks: false,
72+
stubs: false
73+
})
7174

7275
expect(wrapper.is(ComponentAsAClass)).to.equal(true)
7376
})

0 commit comments

Comments
 (0)