Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: automatically extend extended child components #595

Merged
merged 8 commits into from
May 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/en/api/config.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is necessary to add the types to the below files.

}
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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')
})
})