Skip to content

Commit

Permalink
fix: automatically extend extended child components (#595)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyerburgh committed May 11, 2018
1 parent 14c40e6 commit 68a55f1
Show file tree
Hide file tree
Showing 22 changed files with 168 additions and 55 deletions.
15 changes: 15 additions & 0 deletions docs/en/api/config.md
Expand Up @@ -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
```
3 changes: 2 additions & 1 deletion flow/options.flow.js
Expand Up @@ -9,5 +9,6 @@ declare type Options = { // eslint-disable-line no-undef
stubs?: Object,
context?: Object,
attrs?: Object,
listeners?: Object
listeners?: Object,
logModifiedComponents?: Boolean
}
20 changes: 0 additions & 20 deletions packages/create-instance/compile-template.js

This file was deleted.

14 changes: 12 additions & 2 deletions packages/create-instance/create-instance.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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])
})
Expand Down
3 changes: 2 additions & 1 deletion packages/server-test-utils/types/index.d.ts
Expand Up @@ -49,7 +49,8 @@ interface VueTestUtilsConfigOptions {
stubs?: Stubs
mocks?: object
methods?: Record<string, Function>
provide?: object
provide?: object,
logModifiedComponents?: Boolean
}

export declare let config: VueTestUtilsConfigOptions
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/compile-template.js
Expand Up @@ -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))
}
Expand Down
1 change: 1 addition & 0 deletions packages/shared/merge-options.js
Expand Up @@ -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),
Expand Down
3 changes: 2 additions & 1 deletion packages/test-utils/src/config.js
Expand Up @@ -8,5 +8,6 @@ export default {
},
mocks: {},
methods: {},
provide: {}
provide: {},
logModifiedComponents: true
}
2 changes: 1 addition & 1 deletion packages/test-utils/src/index.js
Expand Up @@ -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)
}

Expand Down
3 changes: 2 additions & 1 deletion packages/test-utils/types/index.d.ts
Expand Up @@ -139,7 +139,8 @@ interface VueTestUtilsConfigOptions {
stubs?: Stubs
mocks?: object
methods?: Record<string, Function>
provide?: object
provide?: object,
logModifiedComponents?: Boolean
}

export declare function createLocalVue (): typeof Vue
Expand Down
7 changes: 3 additions & 4 deletions 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]}`)
Expand All @@ -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) {
Expand Down
15 changes: 11 additions & 4 deletions test/specs/components/TransitionStub.spec.js
Expand Up @@ -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
Expand Down Expand Up @@ -48,14 +57,12 @@ describeWithShallowAndMount('TransitionStub', (mountingMethod) => {
`
}
const msg = '[vue-test-utils]: <transition> can only be used on a single element. Use <transition-group> 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', () => {
Expand Down
34 changes: 32 additions & 2 deletions 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: `
Expand Down Expand Up @@ -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: '<span />'
})
const TestComponent = {
template: '<child-component />',
components: {
ChildComponent
}
}
config.logModifiedComponents = false
mountingMethod(TestComponent)
expect(consoleError.called).to.equal(false)
})
})
2 changes: 1 addition & 1 deletion test/specs/create-local-vue.spec.js
Expand Up @@ -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)
Expand Down
25 changes: 25 additions & 0 deletions test/specs/mount.spec.js
Expand Up @@ -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('<div><input /></div>')
const wrapper = mount(compiled)
Expand Down Expand Up @@ -120,6 +130,21 @@ describeIf(process.env.TEST_ENV !== 'node',
expect(wrapper.html()).to.equal(`<div>foo</div>`)
})

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: '<span />'
})
const TestComponent = {
template: '<child-component />',
components: {
ChildComponent
}
}
mount(TestComponent)
expect(consoleError).calledWith(msg)
})

it('deletes mounting options before passing options to component', () => {
const wrapper = mount({
render: h => h('div')
Expand Down
41 changes: 40 additions & 1 deletion test/specs/mounting-options/localVue.spec.js
Expand Up @@ -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(
Expand All @@ -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: '<span>{{val}}</span>',
computed: {
val () {
return this.$store.state.val
}
}
})
const TestComponent = {
template: '<div><child-component /></div>',
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')
}
})
})
1 change: 0 additions & 1 deletion test/specs/mounting-options/methods.spec.js
Expand Up @@ -23,7 +23,6 @@ describeWithMountingMethods('options.methods', (mountingMethod) => {
const HTML = mountingMethod.name === 'renderToString'
? wrapper
: wrapper.html()
console.log(wrapper)
expect(HTML).to.contain('methodFromOptions')
})
})

0 comments on commit 68a55f1

Please sign in to comment.