Skip to content

Commit 68a55f1

Browse files
authoredMay 11, 2018
fix: automatically extend extended child components (#595)
1 parent 14c40e6 commit 68a55f1

22 files changed

+168
-55
lines changed
 

Diff for: ‎docs/en/api/config.md

+15
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,18 @@ VueTestUtils.config.provide['$logger'] = {
7979
}
8080
}
8181
```
82+
83+
### `logModifiedComponents`
84+
85+
- type: `Boolean`
86+
- default: `true`
87+
88+
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.
89+
90+
Example:
91+
92+
```js
93+
import VueTestUtils from '@vue/test-utils'
94+
95+
VueTestUtils.config.logModifiedComponents = false
96+
```

Diff for: ‎flow/options.flow.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ declare type Options = { // eslint-disable-line no-undef
99
stubs?: Object,
1010
context?: Object,
1111
attrs?: Object,
12-
listeners?: Object
12+
listeners?: Object,
13+
logModifiedComponents?: Boolean
1314
}

Diff for: ‎packages/create-instance/compile-template.js

-20
This file was deleted.

Diff for: ‎packages/create-instance/create-instance.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import addListeners from './add-listeners'
99
import addProvide from './add-provide'
1010
import { addEventLogger } from './log-events'
1111
import { createComponentStubs } from 'shared/stub-components'
12-
import { throwError } from 'shared/util'
13-
import { compileTemplate } from './compile-template'
12+
import { throwError, warn } from 'shared/util'
13+
import { compileTemplate } from 'shared/compile-template'
1414
import deleteoptions from './delete-mounting-options'
1515
import createFunctionalComponent from './create-functional-component'
1616
import { componentNeedsCompiling } from 'shared/validators'
@@ -70,6 +70,16 @@ export default function createInstance (
7070
}
7171
}
7272

73+
Object.keys(component.components || {}).forEach((c) => {
74+
if (component.components[c].extendOptions &&
75+
!instanceOptions.components[c]) {
76+
if (options.logModifiedComponents) {
77+
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.`)
78+
}
79+
instanceOptions.components[c] = vue.extend(component.components[c])
80+
}
81+
})
82+
7383
Object.keys(stubComponents).forEach(c => {
7484
vue.component(c, stubComponents[c])
7585
})

Diff for: ‎packages/server-test-utils/types/index.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ interface VueTestUtilsConfigOptions {
4949
stubs?: Stubs
5050
mocks?: object
5151
methods?: Record<string, Function>
52-
provide?: object
52+
provide?: object,
53+
logModifiedComponents?: Boolean
5354
}
5455

5556
export declare let config: VueTestUtilsConfigOptions

Diff for: ‎packages/shared/compile-template.js

+6
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@ export function compileTemplate (component: Component) {
1111
}
1212
})
1313
}
14+
1415
if (component.extends) {
1516
compileTemplate(component.extends)
1617
}
18+
19+
if (component.extendOptions && !component.options.render) {
20+
compileTemplate(component.options)
21+
}
22+
1723
if (component.template) {
1824
Object.assign(component, compileToFunctions(component.template))
1925
}

Diff for: ‎packages/shared/merge-options.js

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export function mergeOptions (
2626
): Options {
2727
return {
2828
...options,
29+
logModifiedComponents: config.logModifiedComponents,
2930
stubs: getOptions('stubs', options.stubs, config),
3031
mocks: getOptions('mocks', options.mocks, config),
3132
methods: getOptions('methods', options.methods, config),

Diff for: ‎packages/test-utils/src/config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export default {
88
},
99
mocks: {},
1010
methods: {},
11-
provide: {}
11+
provide: {},
12+
logModifiedComponents: true
1213
}

Diff for: ‎packages/test-utils/src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import config from './config'
88
import { warn } from 'shared/util'
99

1010
function shallow (component, options) {
11-
warn('shallow has been renamed to shallowMount and will be deprecated in 1.0.0')
11+
warn('shallow has been renamed to shallowMount. shallow will be removed in 1.0.0, use shallowMount instead')
1212
return shallowMount(component, options)
1313
}
1414

Diff for: ‎packages/test-utils/types/index.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ interface VueTestUtilsConfigOptions {
139139
stubs?: Stubs
140140
mocks?: object
141141
methods?: Record<string, Function>
142-
provide?: object
142+
provide?: object,
143+
logModifiedComponents?: Boolean
143144
}
144145

145146
export declare function createLocalVue (): typeof Vue

Diff for: ‎test/resources/utils.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* global describe, it*/
22

33
import Vue from 'vue'
4-
import { shallow, mount } from '~vue/test-utils'
4+
import { shallowMount, mount } from '~vue/test-utils'
55
import { renderToString } from '~vue/server-test-utils'
66

77
export const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`)
@@ -28,11 +28,10 @@ export const scopedSlotsSupported = vueVersion > 2
2828

2929
const shallowAndMount = process.env.TEST_ENV === 'node'
3030
? []
31-
: [mount, shallow]
32-
console.log(shallowAndMount)
31+
: [mount, shallowMount]
3332
const shallowMountAndRender = process.env.TEST_ENV === 'node'
3433
? [renderToString]
35-
: [mount, shallow]
34+
: [mount, shallowMount]
3635

3736
export function describeWithShallowAndMount (spec, cb) {
3837
if (shallowAndMount.length > 0) {

Diff for: ‎test/specs/components/TransitionStub.spec.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ import { describeWithShallowAndMount } from '~resources/utils'
33
import { TransitionStub } from '~vue/test-utils'
44

55
describeWithShallowAndMount('TransitionStub', (mountingMethod) => {
6+
let consoleError
7+
8+
beforeEach(() => {
9+
consoleError = sinon.stub(console, 'error')
10+
})
11+
12+
afterEach(() => {
13+
consoleError.restore()
14+
})
15+
616
it('update synchronously when used as stubs for Transition', () => {
7-
console.log(TransitionStub)
817
const wrapper = mountingMethod(ComponentWithTransition, {
918
stubs: {
1019
'transition': TransitionStub
@@ -48,14 +57,12 @@ describeWithShallowAndMount('TransitionStub', (mountingMethod) => {
4857
`
4958
}
5059
const msg = '[vue-test-utils]: <transition> can only be used on a single element. Use <transition-group> for lists.'
51-
const error = sinon.stub(console, 'error')
5260
mountingMethod(TestComponent, {
5361
stubs: {
5462
'transition': TransitionStub
5563
}
5664
})
57-
expect(error).calledWith(msg)
58-
error.restore()
65+
expect(consoleError).calledWith(msg)
5966
})
6067

6168
it('handles keyed transitions', () => {

Diff for: ‎test/specs/config.spec.js

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
1-
import { describeWithShallowAndMount, itDoNotRunIf } from '~resources/utils'
1+
import {
2+
describeWithShallowAndMount,
3+
itDoNotRunIf,
4+
itSkipIf,
5+
vueVersion
6+
} from '~resources/utils'
27
import { config, TransitionStub, TransitionGroupStub, createLocalVue } from '~vue/test-utils'
8+
import Vue from 'vue'
39

410
describeWithShallowAndMount('config', (mountingMethod) => {
511
let configStubsSave
12+
let consoleError
13+
let configLogSave
14+
615
beforeEach(() => {
716
TransitionGroupStub.name = 'another-temp-name'
817
TransitionStub.name = 'a-temp-name'
918
configStubsSave = config.stubs
19+
configLogSave = config.logModifiedComponents
20+
consoleError = sinon.stub(console, 'error')
1021
})
1122

1223
afterEach(() => {
1324
TransitionGroupStub.name = 'transition-group'
1425
TransitionStub.name = 'transition'
1526
config.stubs = configStubsSave
27+
config.logModifiedComponents = configLogSave
28+
consoleError.restore()
1629
})
1730

18-
itDoNotRunIf(mountingMethod.name === 'shallow',
31+
itDoNotRunIf(mountingMethod.name === 'shallowMount',
1932
'stubs transition and transition-group by default', () => {
2033
const testComponent = {
2134
template: `
@@ -121,4 +134,21 @@ describeWithShallowAndMount('config', (mountingMethod) => {
121134
expect(wrapper.contains(TransitionGroupStub)).to.equal(false)
122135
expect(wrapper.contains(TransitionStub)).to.equal(false)
123136
})
137+
138+
itSkipIf(
139+
vueVersion < 2.3,
140+
'does not log when component is extended if logModifiedComponents is false', () => {
141+
const ChildComponent = Vue.extend({
142+
template: '<span />'
143+
})
144+
const TestComponent = {
145+
template: '<child-component />',
146+
components: {
147+
ChildComponent
148+
}
149+
}
150+
config.logModifiedComponents = false
151+
mountingMethod(TestComponent)
152+
expect(consoleError.called).to.equal(false)
153+
})
124154
})

Diff for: ‎test/specs/create-local-vue.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describeWithShallowAndMount('createLocalVue', (mountingMethod) => {
7070
})
7171

7272
itDoNotRunIf(
73-
mountingMethod.name === 'shallow',
73+
mountingMethod.name === 'shallowMount',
7474
'Router should work properly with local Vue', () => {
7575
const localVue = createLocalVue()
7676
localVue.use(VueRouter)

Diff for: ‎test/specs/mount.spec.js

+25
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import { injectSupported, vueVersion, describeIf } from '~resources/utils'
88

99
describeIf(process.env.TEST_ENV !== 'node',
1010
'mount', () => {
11+
let consoleError
12+
13+
beforeEach(() => {
14+
consoleError = sinon.stub(console, 'error')
15+
})
16+
17+
afterEach(() => {
18+
consoleError.restore()
19+
})
20+
1121
it('returns new VueWrapper with mounted Vue instance if no options are passed', () => {
1222
const compiled = compileToFunctions('<div><input /></div>')
1323
const wrapper = mount(compiled)
@@ -120,6 +130,21 @@ describeIf(process.env.TEST_ENV !== 'node',
120130
expect(wrapper.html()).to.equal(`<div>foo</div>`)
121131
})
122132

133+
it('logs if component is extended', () => {
134+
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.'
135+
const ChildComponent = Vue.extend({
136+
template: '<span />'
137+
})
138+
const TestComponent = {
139+
template: '<child-component />',
140+
components: {
141+
ChildComponent
142+
}
143+
}
144+
mount(TestComponent)
145+
expect(consoleError).calledWith(msg)
146+
})
147+
123148
it('deletes mounting options before passing options to component', () => {
124149
const wrapper = mount({
125150
render: h => h('div')

Diff for: ‎test/specs/mounting-options/localVue.spec.js

+40-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import Vue from 'vue'
22
import {
33
describeWithMountingMethods,
44
itSkipIf,
5-
isRunningPhantomJS
5+
isRunningPhantomJS,
6+
vueVersion
67
} from '~resources/utils'
8+
import { createLocalVue } from '~vue/test-utils'
9+
import Vuex from 'vuex'
710

811
describeWithMountingMethods('options.localVue', (mountingMethod) => {
912
itSkipIf(
@@ -30,4 +33,40 @@ describeWithMountingMethods('options.localVue', (mountingMethod) => {
3033
: freshWrapper.html()
3134
expect(freshHTML).to.not.contain('some value')
3235
})
36+
37+
itSkipIf(
38+
vueVersion < 2.3,
39+
'works correctly with extended children', () => {
40+
const localVue = createLocalVue()
41+
localVue.use(Vuex)
42+
const store = new Vuex.Store({
43+
state: { val: 2 }
44+
})
45+
const ChildComponent = Vue.extend({
46+
template: '<span>{{val}}</span>',
47+
computed: {
48+
val () {
49+
return this.$store.state.val
50+
}
51+
}
52+
})
53+
const TestComponent = {
54+
template: '<div><child-component /></div>',
55+
components: {
56+
ChildComponent
57+
}
58+
}
59+
const wrapper = mountingMethod(TestComponent, {
60+
localVue,
61+
store
62+
})
63+
const HTML = mountingMethod.name === 'renderToString'
64+
? wrapper
65+
: wrapper.html()
66+
if (mountingMethod.name === 'shallowMount') {
67+
expect(HTML).to.not.contain('2')
68+
} else {
69+
expect(HTML).to.contain('2')
70+
}
71+
})
3372
})

Diff for: ‎test/specs/mounting-options/methods.spec.js

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ describeWithMountingMethods('options.methods', (mountingMethod) => {
2323
const HTML = mountingMethod.name === 'renderToString'
2424
? wrapper
2525
: wrapper.html()
26-
console.log(wrapper)
2726
expect(HTML).to.contain('methodFromOptions')
2827
})
2928
})

Diff for: ‎test/specs/mounting-options/mocks.spec.js

+7-9
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ import {
88

99
describeWithMountingMethods('options.mocks', (mountingMethod) => {
1010
let configMocksSave
11+
let consoleError
1112

1213
beforeEach(() => {
1314
configMocksSave = config.mocks
1415
config.mocks = {}
16+
consoleError = sinon.stub(console, 'error')
1517
})
1618

1719
afterEach(() => {
1820
config.mocks = configMocksSave
21+
consoleError.restore()
1922
})
2023

2124
it('adds variables to vm when passed', () => {
@@ -71,7 +74,7 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => {
7174
expect(wrapper.text()).to.contain('changed value')
7275
})
7376

74-
itDoNotRunIf(mountingMethod.name === 'shallow',
77+
itDoNotRunIf(mountingMethod.name === 'shallowMount',
7578
'adds variables available to nested vms', () => {
7679
const count = 1
7780
const wrapper = mountingMethod({
@@ -88,7 +91,7 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => {
8891
expect(HTML).contains(count)
8992
})
9093

91-
itDoNotRunIf(mountingMethod.name === 'shallow',
94+
itDoNotRunIf(mountingMethod.name === 'shallowMount',
9295
'adds variables available to nested vms using localVue', () => {
9396
const localVue = createLocalVue()
9497
const count = 1
@@ -121,7 +124,6 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => {
121124
})
122125

123126
it('logs that a property cannot be overwritten if there are problems writing', () => {
124-
const error = sinon.stub(console, 'error')
125127
const localVue = createLocalVue()
126128
Object.defineProperty(localVue.prototype, '$store', {
127129
value: 42
@@ -134,8 +136,7 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => {
134136
}
135137
})
136138
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'
137-
expect(error.calledWith(msg)).to.equal(true)
138-
error.restore()
139+
expect(consoleError.calledWith(msg)).to.equal(true)
139140
})
140141

141142
it('prioritize mounting options over config', () => {
@@ -155,12 +156,10 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => {
155156
const HTML = mountingMethod.name === 'renderToString'
156157
? wrapper
157158
: wrapper.html()
158-
console.log(wrapper)
159159
expect(HTML).to.contain('locallyMockedValue')
160160
})
161161

162162
it('logs that a property cannot be overwritten if there are problems writing', () => {
163-
const error = sinon.stub(console, 'error')
164163
const localVue = createLocalVue()
165164
Object.defineProperty(localVue.prototype, '$val', {
166165
value: 42
@@ -173,7 +172,6 @@ describeWithMountingMethods('options.mocks', (mountingMethod) => {
173172
}
174173
})
175174
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'
176-
expect(error.calledWith(msg)).to.equal(true)
177-
error.restore()
175+
expect(consoleError.calledWith(msg)).to.equal(true)
178176
})
179177
})

Diff for: ‎test/specs/mounting-options/stubs.spec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describeWithMountingMethods('options.stub', (mountingMethod) => {
8484
})
8585
})
8686

87-
itDoNotRunIf(mountingMethod.name === 'shallow' ||
87+
itDoNotRunIf(mountingMethod.name === 'shallowMount' ||
8888
mountingMethod.name === 'renderToString',
8989
'does not modify component directly', () => {
9090
const wrapper = mountingMethod(ComponentWithNestedChildren, {
@@ -178,7 +178,7 @@ describeWithMountingMethods('options.stub', (mountingMethod) => {
178178
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = compilerSave
179179
})
180180

181-
itDoNotRunIf(mountingMethod.name === 'shallow',
181+
itDoNotRunIf(mountingMethod.name === 'shallowMount',
182182
'does not stub component when set to false', () => {
183183
const wrapper = mountingMethod(ComponentWithChild, {
184184
stubs: {
@@ -249,7 +249,7 @@ describeWithMountingMethods('options.stub', (mountingMethod) => {
249249
})
250250

251251
itDoNotRunIf(
252-
mountingMethod.name === 'shallow' ||
252+
mountingMethod.name === 'shallowMount' ||
253253
mountingMethod.name === 'renderToString',
254254
'stubs on child components', () => {
255255
const TestComponent = {

Diff for: ‎test/specs/shallow-mount.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import RecursiveComponent from '~resources/components/recursive-component.vue'
1111
import { vueVersion, describeIf } from '~resources/utils'
1212

1313
describeIf(process.env.TEST_ENV !== 'node',
14-
'shallow', () => {
14+
'shallowMount', () => {
1515
let info
1616

1717
beforeEach(() => {

Diff for: ‎test/specs/shallow.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import RecursiveComponent from '~resources/components/recursive-component.vue'
1111
import { vueVersion, describeIf } from '~resources/utils'
1212

1313
describeIf(process.env.TEST_ENV !== 'node',
14-
'shallow', () => {
14+
'shallowMount', () => {
1515
let info
1616

1717
beforeEach(() => {

Diff for: ‎test/specs/wrapper/html.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describeWithShallowAndMount('html', (mountingMethod) => {
1111
})
1212

1313
it('returns a VueWrappers HTML as a string when component has no render function', () => {
14-
if (mountingMethod.name === 'shallow') return
14+
if (mountingMethod.name === 'shallowMount') return
1515
const wrapper = mountingMethod({
1616
template: `<div>1<tester></tester></div>`,
1717
components: {

0 commit comments

Comments
 (0)
Please sign in to comment.