Skip to content

Commit 20465ab

Browse files
authored
fix: find functional components in shallow (#408)
1 parent f8ce240 commit 20465ab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+490
-474
lines changed

dist/vue-test-utils.js

+127-108
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
"setup": "node build/install-hooks.js",
2323
"test": "npm run lint && npm run lint:docs && npm run flow && npm run test:types && npm run test:unit && npm run test:unit:karma",
2424
"test:compat": "test/test.sh",
25-
"test:unit": "npm run build:test && cross-env BABEL_ENV=test && mocha-webpack --webpack-config build/webpack.test.config.js test/unit/specs --recursive --require test/unit/setup/mocha.setup.js",
26-
"test:unit:karma": "npm run build:test && cross-env BABEL_ENV=test TARGET=browser karma start test/unit/setup/karma.conf.js --single-run",
25+
"test:unit": "npm run build:test && cross-env BABEL_ENV=test && mocha-webpack --webpack-config build/webpack.test.config.js test/specs --recursive --require test/setup/mocha.setup.js",
26+
"test:unit:karma": "npm run build:test && cross-env BABEL_ENV=test TARGET=browser karma start test/setup/karma.conf.js --single-run",
2727
"test:types": "tsc -p types",
2828
"release": "bash build/release.sh",
2929
"release:note": "node build/gen-release-note.js"

src/lib/find-vue-components.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export function vmFunctionalCtorMatchesSelector (component: VNode, Ctor: Object)
7070
throwError('find for functional components is not support in Vue < 2.3')
7171
}
7272

73+
if (!Ctor) {
74+
return false
75+
}
76+
7377
if (!component[FUNCTIONAL_OPTIONS]) {
7478
return false
7579
}
@@ -86,7 +90,10 @@ export default function findVueComponents (
8690
const nodes = root._vnode
8791
? findAllFunctionalComponentsFromVnode(root._vnode)
8892
: findAllFunctionalComponentsFromVnode(root)
89-
return nodes.filter(node => vmFunctionalCtorMatchesSelector(node, selector._Ctor))
93+
return nodes.filter(node =>
94+
vmFunctionalCtorMatchesSelector(node, selector._Ctor) ||
95+
node[FUNCTIONAL_OPTIONS].name === selector.name
96+
)
9097
}
9198
const components = root._isVue
9299
? findAllVueComponentsFromVm(root)

src/lib/stub-components.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ function getCoreProperties (component: Component): Object {
3535
staticStyle: component.staticStyle,
3636
style: component.style,
3737
normalizedStyle: component.normalizedStyle,
38-
nativeOn: component.nativeOn
38+
nativeOn: component.nativeOn,
39+
functional: component.functional
3940
}
4041
}
4142
function createStubFromString (templateString: string, originalComponent: Component): Object {
@@ -51,7 +52,7 @@ function createStubFromString (templateString: string, originalComponent: Compon
5152
function createBlankStub (originalComponent: Component) {
5253
return {
5354
...getCoreProperties(originalComponent),
54-
render: () => {}
55+
render: h => h('')
5556
}
5657
}
5758

@@ -126,6 +127,7 @@ function stubComponents (components: Object, stubbedComponents: Object) {
126127
Object.keys(components).forEach(component => {
127128
// Remove cached constructor
128129
delete components[component]._Ctor
130+
console.log(components[component].name)
129131
if (!components[component].name) {
130132
components[component].name = component
131133
}

test/resources/test-utils.js

+29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
/* global describe, it*/
2+
13
import Vue from 'vue'
4+
import { shallow, mount } from '~vue-test-utils'
25

36
export const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`)
47

@@ -17,3 +20,29 @@ export function listenersSupported () {
1720
export function functionalSFCsSupported () {
1821
return vueVersion >= 2.5
1922
}
23+
24+
export function describeWithShallowAndMount (spec, cb) {
25+
;[mount, shallow].forEach(method => {
26+
describe(`${spec} with ${method.name}`, () => cb(method))
27+
})
28+
}
29+
30+
describeWithShallowAndMount.skip = function (spec, cb) {
31+
;[mount, shallow].forEach(method => {
32+
describe.skip(`${spec} with ${method.name}`, () => cb(method))
33+
})
34+
}
35+
36+
describeWithShallowAndMount.only = function (spec, cb) {
37+
;[mount, shallow].forEach(method => {
38+
describe.only(`${spec} with ${method.name}`, () => cb(method))
39+
})
40+
}
41+
42+
export function itSkipIf (predicate, spec, cb) {
43+
if (predicate) {
44+
it.skip(spec, cb)
45+
} else {
46+
it(spec, cb)
47+
}
48+
}

test/unit/setup/karma.conf.js renamed to test/setup/karma.conf.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
const webpackConfig = require('../../../build/webpack.test.config.js')
1+
const webpackConfig = require('../../build/webpack.test.config.js')
22

33
module.exports = function (config) {
44
config.set({
55
browsers: ['ChromeHeadless'],
66
frameworks: ['mocha', 'sinon-chai'],
77
reporters: ['spec'],
88
files: [
9-
'../../../node_modules/babel-polyfill/dist/polyfill.js',
9+
'../../node_modules/babel-polyfill/dist/polyfill.js',
1010
'../specs/**/*.+(vue|js)'
1111
],
1212
preprocessors: {
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

test/unit/specs/mount/options/slots.spec.js renamed to test/specs/mounting-options/slots.spec.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ describe('mount.slots', () => {
6969
it('throws error if passed string in default slot object and vue-template-compiler is undefined', () => {
7070
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
7171
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
72-
delete require.cache[require.resolve('../../../../../src/mount')]
73-
const mountFresh = require('../../../../../src/mount').default
72+
delete require.cache[require.resolve('../../../src/mount')]
73+
const mountFresh = require('../../../src/mount').default
7474
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
7575
const fn = () => mountFresh(ComponentWithSlots, { slots: { default: '<span />' }})
7676
try {
@@ -95,8 +95,8 @@ describe('mount.slots', () => {
9595
it('throws error if passed string in default slot array vue-template-compiler is undefined', () => {
9696
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
9797
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
98-
delete require.cache[require.resolve('../../../../../src/mount')]
99-
const mountFresh = require('../../../../../src/mount').default
98+
delete require.cache[require.resolve('../../../src/mount')]
99+
const mountFresh = require('../../../src/mount').default
100100
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
101101
const fn = () => mountFresh(ComponentWithSlots, { slots: { default: ['<span />'] }})
102102
try {
@@ -255,8 +255,8 @@ describe('mount.slots', () => {
255255
}
256256
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
257257
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
258-
delete require.cache[require.resolve('../../../../../src/mount')]
259-
const mountFresh = require('../../../../../src/mount').default
258+
delete require.cache[require.resolve('../../../src/mount')]
259+
const mountFresh = require('../../../src/mount').default
260260
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
261261
const fn = () => mountFresh(TestComponent, { slots: { default: ['<span />'] }})
262262
try {

test/unit/specs/mount/options/stubs.spec.js renamed to test/specs/mounting-options/stubs.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ describe('mount.stub', () => {
134134
it('throws error if passed string in object when vue-template-compiler is undefined', () => {
135135
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
136136
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
137-
delete require.cache[require.resolve('../../../../../src/mount')]
138-
const mountFresh = require('../../../../../src/mount').default
137+
delete require.cache[require.resolve('../../../src/mount')]
138+
const mountFresh = require('../../../src/mount').default
139139
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
140140
const fn = () => mountFresh(Component, {
141141
stubs: {
File renamed without changes.

test/unit/specs/mount/Wrapper/at.spec.js renamed to test/specs/wrapper/at.spec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { compileToFunctions } from 'vue-template-compiler'
2-
import { mount } from '~vue-test-utils'
2+
import { describeWithShallowAndMount } from '~resources/test-utils'
33

4-
describe('at', () => {
4+
describeWithShallowAndMount('at', (mountingMethod) => {
55
it('throws an error', () => {
66
const compiled = compileToFunctions('<div />')
7-
const wrapper = mount(compiled)
7+
const wrapper = mountingMethod(compiled)
88
const message = '[vue-test-utils]: at() must be called on a WrapperArray'
99
const fn = () => wrapper.at()
1010
expect(fn).to.throw().with.property('message', message)

test/unit/specs/mount/Wrapper/attributes.spec.js renamed to test/specs/wrapper/attributes.spec.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import { compileToFunctions } from 'vue-template-compiler'
2-
import { mount } from '~vue-test-utils'
2+
import { describeWithShallowAndMount } from '~resources/test-utils'
33

4-
describe('attributes', () => {
4+
describeWithShallowAndMount('attributes', (mountingMethod) => {
55
it('returns true if wrapper contains attribute matching value', () => {
66
const attribute = 'attribute'
77
const value = 'value'
88
const compiled = compileToFunctions(`<div ${attribute}=${value}></div>`)
9-
const wrapper = mount(compiled)
9+
const wrapper = mountingMethod(compiled)
1010
expect(wrapper.attributes()).to.eql({ attribute: value })
1111
})
1212

1313
it('returns empty object if wrapper does not contain any attributes', () => {
1414
const compiled = compileToFunctions('<div />')
15-
const wrapper = mount(compiled)
15+
const wrapper = mountingMethod(compiled)
1616
expect(wrapper.attributes()).to.eql({})
1717
})
1818

1919
it('returns empty object if wrapper element is null', () => {
2020
const compiled = compileToFunctions('<div />')
21-
const wrapper = mount(compiled)
21+
const wrapper = mountingMethod(compiled)
2222
wrapper.element = null
2323
expect(wrapper.attributes()).to.eql({})
2424
})
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1-
1+
import { describeWithShallowAndMount } from '~resources/test-utils'
22
import { compileToFunctions } from 'vue-template-compiler'
3-
import { mount } from '~vue-test-utils'
43
import ComponentWithCssModules from '~resources/components/component-with-css-modules.vue'
54

6-
describe('classes', () => {
5+
describeWithShallowAndMount('classes', (mountingMethod) => {
76
it('returns array of class names if wrapper has class names', () => {
87
const compiled = compileToFunctions('<div class="a-class b-class" />')
9-
const wrapper = mount(compiled)
8+
const wrapper = mountingMethod(compiled)
109
expect(wrapper.classes()).to.contain('a-class')
1110
expect(wrapper.classes()).to.contain('b-class')
1211
})
1312

1413
it('returns empty array if wrapper has no classes', () => {
1514
const compiled = compileToFunctions('<div />')
16-
const wrapper = mount(compiled)
15+
const wrapper = mountingMethod(compiled)
1716
expect(wrapper.classes().length).to.equal(0)
1817
})
1918

2019
it('returns original class names when element mapped in css modules', () => {
21-
const wrapper = mount(ComponentWithCssModules)
20+
const wrapper = mountingMethod(ComponentWithCssModules)
2221
expect(wrapper.classes()).to.eql(['extension', 'color-red'])
2322
})
2423
})

test/unit/specs/mount/Wrapper/contains.spec.js renamed to test/specs/wrapper/contains.spec.js

+23-18
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import { compileToFunctions } from 'vue-template-compiler'
2-
import { mount } from '~vue-test-utils'
32
import ComponentWithChild from '~resources/components/component-with-child.vue'
43
import Component from '~resources/components/component.vue'
54
import FunctionalComponent from '~resources/components/functional-component.vue'
65
import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
7-
import { functionalSFCsSupported } from '~resources/test-utils'
6+
import {
7+
functionalSFCsSupported,
8+
describeWithShallowAndMount,
9+
itSkipIf
10+
} from '~resources/test-utils'
811
import ComponentWithoutName from '~resources/components/component-without-name.vue'
912

10-
describe('contains', () => {
13+
describeWithShallowAndMount('contains', (mountingMethod) => {
1114
it('returns true if wrapper contains element', () => {
1215
const compiled = compileToFunctions('<div><input /></div>')
13-
const wrapper = mount(compiled)
16+
const wrapper = mountingMethod(compiled)
1417
expect(wrapper.contains('input')).to.equal(true)
1518
})
1619

1720
it('returns true if wrapper contains Vue component', () => {
18-
const wrapper = mount(ComponentWithChild)
21+
const wrapper = mountingMethod(ComponentWithChild)
1922
expect(wrapper.contains(Component)).to.equal(true)
2023
})
2124

@@ -33,11 +36,13 @@ describe('contains', () => {
3336
FunctionalComponent
3437
}
3538
}
36-
const wrapper = mount(TestComponent)
39+
40+
const wrapper = mountingMethod(TestComponent)
3741
expect(wrapper.contains(FunctionalComponent)).to.equal(true)
3842
})
3943

40-
it('returns true if wrapper contains Vue class component', () => {
44+
itSkipIf(mountingMethod.name === 'shallow',
45+
'returns true if wrapper contains Vue class component', () => {
4146
const TestComponent = {
4247
template: `
4348
<div>
@@ -48,19 +53,19 @@ describe('contains', () => {
4853
ComponentAsAClass
4954
}
5055
}
51-
const wrapper = mount(TestComponent)
56+
const wrapper = mountingMethod(TestComponent)
5257
expect(wrapper.contains(ComponentAsAClass)).to.equal(true)
5358
})
5459

5560
it('returns true if wrapper contains element specified by ref selector', () => {
5661
const compiled = compileToFunctions('<div><input ref="foo" /></div>')
57-
const wrapper = mount(compiled)
62+
const wrapper = mountingMethod(compiled)
5863
expect(wrapper.contains({ ref: 'foo' })).to.equal(true)
5964
})
6065

6166
it('throws an error when ref selector is called on a wrapper that is not a Vue component', () => {
6267
const compiled = compileToFunctions('<div><a href="/"></a></div>')
63-
const wrapper = mount(compiled)
68+
const wrapper = mountingMethod(compiled)
6469
const a = wrapper.find('a')
6570
const message = '[vue-test-utils]: $ref selectors can only be used on Vue component wrappers'
6671
const fn = () => a.contains({ ref: 'foo' })
@@ -69,13 +74,13 @@ describe('contains', () => {
6974

7075
it('returns true when wrapper contains root element', () => {
7176
const compiled = compileToFunctions('<div><input /></div>')
72-
const wrapper = mount(compiled)
77+
const wrapper = mountingMethod(compiled)
7378
expect(wrapper.contains('doesntexist')).to.equal(false)
7479
})
7580

7681
it('returns true if wrapper root element matches contains', () => {
7782
const compiled = compileToFunctions('<div><input /></div>')
78-
const wrapper = mount(compiled)
83+
const wrapper = mountingMethod(compiled)
7984
expect(wrapper.contains('doesntexist')).to.equal(false)
8085
})
8186

@@ -90,24 +95,24 @@ describe('contains', () => {
9095
ComponentWithoutName
9196
}
9297
}
93-
const wrapper = mount(TestComponent)
98+
const wrapper = mountingMethod(TestComponent)
9499
expect(wrapper.contains(ComponentWithoutName)).to.equal(true)
95100
})
96101

97102
it('returns true if wrapper root Component matches selector', () => {
98-
const wrapper = mount(Component)
103+
const wrapper = mountingMethod(Component)
99104
expect(wrapper.contains(Component)).to.equal(true)
100105
})
101106

102107
it('returns false if wrapper does not contain element', () => {
103108
const compiled = compileToFunctions('<div></div>')
104-
const wrapper = mount(compiled)
109+
const wrapper = mountingMethod(compiled)
105110
expect(wrapper.contains('div')).to.equal(true)
106111
})
107112

108113
it('returns false if wrapper does not contain element specified by ref selector', () => {
109114
const compiled = compileToFunctions('<div><input ref="bar" /></div>')
110-
const wrapper = mount(compiled)
115+
const wrapper = mountingMethod(compiled)
111116
expect(wrapper.contains({ ref: 'foo' })).to.equal(false)
112117
})
113118

@@ -121,13 +126,13 @@ describe('contains', () => {
121126
})
122127
}
123128
}
124-
const wrapper = mount(TestComponent)
129+
const wrapper = mountingMethod(TestComponent)
125130
expect(wrapper.contains('svg')).to.equal(true)
126131
expect(wrapper.find('svg').contains('svg')).to.equal(true)
127132
})
128133

129134
it('throws an error if selector is not a valid selector', () => {
130-
const wrapper = mount(Component)
135+
const wrapper = mountingMethod(Component)
131136
const invalidSelectors = [
132137
undefined, null, NaN, 0, 2, true, false, () => {}, {}, { name: undefined }, { ref: 'foo', nope: true }, []
133138
]

test/unit/specs/mount/Wrapper/destroy.spec.js renamed to test/specs/wrapper/destroy.spec.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { compileToFunctions } from 'vue-template-compiler'
2-
import { mount } from '~vue-test-utils'
2+
import { describeWithShallowAndMount } from '~resources/test-utils'
33
import sinon from 'sinon'
44

5-
describe('destroy', () => {
5+
describeWithShallowAndMount('destroy', (mountingMethod) => {
66
it('should trigger beforeDestroy ', () => {
77
const spy = sinon.stub()
8-
mount({
8+
mountingMethod({
99
render: null,
1010
beforeDestroy () {
1111
spy()
@@ -16,7 +16,7 @@ describe('destroy', () => {
1616

1717
it('should trigger destroy ', () => {
1818
const spy = sinon.stub()
19-
mount({
19+
mountingMethod({
2020
render: null,
2121
destroyed () {
2222
spy()
@@ -27,7 +27,7 @@ describe('destroy', () => {
2727

2828
it('should remove element from document.body', () => {
2929
const compiled = compileToFunctions('<div></div>')
30-
const wrapper = mount(compiled, { attachToDocument: true })
30+
const wrapper = mountingMethod(compiled, { attachToDocument: true })
3131
expect(wrapper.vm.$el.parentNode).to.equal(document.body)
3232
wrapper.destroy()
3333
expect(wrapper.vm.$el.parentNode).to.be.null

0 commit comments

Comments
 (0)