Skip to content

Commit

Permalink
fix: find functional components in shallow (#408)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyerburgh authored Feb 3, 2018
1 parent f8ce240 commit 20465ab
Show file tree
Hide file tree
Showing 78 changed files with 490 additions and 474 deletions.
235 changes: 127 additions & 108 deletions dist/vue-test-utils.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"setup": "node build/install-hooks.js",
"test": "npm run lint && npm run lint:docs && npm run flow && npm run test:types && npm run test:unit && npm run test:unit:karma",
"test:compat": "test/test.sh",
"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",
"test:unit:karma": "npm run build:test && cross-env BABEL_ENV=test TARGET=browser karma start test/unit/setup/karma.conf.js --single-run",
"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",
"test:unit:karma": "npm run build:test && cross-env BABEL_ENV=test TARGET=browser karma start test/setup/karma.conf.js --single-run",
"test:types": "tsc -p types",
"release": "bash build/release.sh",
"release:note": "node build/gen-release-note.js"
Expand Down
9 changes: 8 additions & 1 deletion src/lib/find-vue-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export function vmFunctionalCtorMatchesSelector (component: VNode, Ctor: Object)
throwError('find for functional components is not support in Vue < 2.3')
}

if (!Ctor) {
return false
}

if (!component[FUNCTIONAL_OPTIONS]) {
return false
}
Expand All @@ -86,7 +90,10 @@ export default function findVueComponents (
const nodes = root._vnode
? findAllFunctionalComponentsFromVnode(root._vnode)
: findAllFunctionalComponentsFromVnode(root)
return nodes.filter(node => vmFunctionalCtorMatchesSelector(node, selector._Ctor))
return nodes.filter(node =>
vmFunctionalCtorMatchesSelector(node, selector._Ctor) ||
node[FUNCTIONAL_OPTIONS].name === selector.name
)
}
const components = root._isVue
? findAllVueComponentsFromVm(root)
Expand Down
6 changes: 4 additions & 2 deletions src/lib/stub-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ function getCoreProperties (component: Component): Object {
staticStyle: component.staticStyle,
style: component.style,
normalizedStyle: component.normalizedStyle,
nativeOn: component.nativeOn
nativeOn: component.nativeOn,
functional: component.functional
}
}
function createStubFromString (templateString: string, originalComponent: Component): Object {
Expand All @@ -51,7 +52,7 @@ function createStubFromString (templateString: string, originalComponent: Compon
function createBlankStub (originalComponent: Component) {
return {
...getCoreProperties(originalComponent),
render: () => {}
render: h => h('')
}
}

Expand Down Expand Up @@ -126,6 +127,7 @@ function stubComponents (components: Object, stubbedComponents: Object) {
Object.keys(components).forEach(component => {
// Remove cached constructor
delete components[component]._Ctor
console.log(components[component].name)
if (!components[component].name) {
components[component].name = component
}
Expand Down
29 changes: 29 additions & 0 deletions test/resources/test-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/* global describe, it*/

import Vue from 'vue'
import { shallow, mount } from '~vue-test-utils'

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

Expand All @@ -17,3 +20,29 @@ export function listenersSupported () {
export function functionalSFCsSupported () {
return vueVersion >= 2.5
}

export function describeWithShallowAndMount (spec, cb) {
;[mount, shallow].forEach(method => {
describe(`${spec} with ${method.name}`, () => cb(method))
})
}

describeWithShallowAndMount.skip = function (spec, cb) {
;[mount, shallow].forEach(method => {
describe.skip(`${spec} with ${method.name}`, () => cb(method))
})
}

describeWithShallowAndMount.only = function (spec, cb) {
;[mount, shallow].forEach(method => {
describe.only(`${spec} with ${method.name}`, () => cb(method))
})
}

export function itSkipIf (predicate, spec, cb) {
if (predicate) {
it.skip(spec, cb)
} else {
it(spec, cb)
}
}
4 changes: 2 additions & 2 deletions test/unit/setup/karma.conf.js → test/setup/karma.conf.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const webpackConfig = require('../../../build/webpack.test.config.js')
const webpackConfig = require('../../build/webpack.test.config.js')

module.exports = function (config) {
config.set({
browsers: ['ChromeHeadless'],
frameworks: ['mocha', 'sinon-chai'],
reporters: ['spec'],
files: [
'../../../node_modules/babel-polyfill/dist/polyfill.js',
'../../node_modules/babel-polyfill/dist/polyfill.js',
'../specs/**/*.+(vue|js)'
],
preprocessors: {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ describe('mount.slots', () => {
it('throws error if passed string in default slot object and vue-template-compiler is undefined', () => {
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
delete require.cache[require.resolve('../../../../../src/mount')]
const mountFresh = require('../../../../../src/mount').default
delete require.cache[require.resolve('../../../src/mount')]
const mountFresh = require('../../../src/mount').default
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
const fn = () => mountFresh(ComponentWithSlots, { slots: { default: '<span />' }})
try {
Expand All @@ -95,8 +95,8 @@ describe('mount.slots', () => {
it('throws error if passed string in default slot array vue-template-compiler is undefined', () => {
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
delete require.cache[require.resolve('../../../../../src/mount')]
const mountFresh = require('../../../../../src/mount').default
delete require.cache[require.resolve('../../../src/mount')]
const mountFresh = require('../../../src/mount').default
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
const fn = () => mountFresh(ComponentWithSlots, { slots: { default: ['<span />'] }})
try {
Expand Down Expand Up @@ -255,8 +255,8 @@ describe('mount.slots', () => {
}
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
delete require.cache[require.resolve('../../../../../src/mount')]
const mountFresh = require('../../../../../src/mount').default
delete require.cache[require.resolve('../../../src/mount')]
const mountFresh = require('../../../src/mount').default
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
const fn = () => mountFresh(TestComponent, { slots: { default: ['<span />'] }})
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ describe('mount.stub', () => {
it('throws error if passed string in object when vue-template-compiler is undefined', () => {
const compilerSave = require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions
require.cache[require.resolve('vue-template-compiler')].exports.compileToFunctions = undefined
delete require.cache[require.resolve('../../../../../src/mount')]
const mountFresh = require('../../../../../src/mount').default
delete require.cache[require.resolve('../../../src/mount')]
const mountFresh = require('../../../src/mount').default
const message = '[vue-test-utils]: vueTemplateCompiler is undefined, you must pass components explicitly if vue-template-compiler is undefined'
const fn = () => mountFresh(Component, {
stubs: {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { compileToFunctions } from 'vue-template-compiler'
import { mount } from '~vue-test-utils'
import { describeWithShallowAndMount } from '~resources/test-utils'

describe('at', () => {
describeWithShallowAndMount('at', (mountingMethod) => {
it('throws an error', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
const message = '[vue-test-utils]: at() must be called on a WrapperArray'
const fn = () => wrapper.at()
expect(fn).to.throw().with.property('message', message)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { compileToFunctions } from 'vue-template-compiler'
import { mount } from '~vue-test-utils'
import { describeWithShallowAndMount } from '~resources/test-utils'

describe('attributes', () => {
describeWithShallowAndMount('attributes', (mountingMethod) => {
it('returns true if wrapper contains attribute matching value', () => {
const attribute = 'attribute'
const value = 'value'
const compiled = compileToFunctions(`<div ${attribute}=${value}></div>`)
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.attributes()).to.eql({ attribute: value })
})

it('returns empty object if wrapper does not contain any attributes', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.attributes()).to.eql({})
})

it('returns empty object if wrapper element is null', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
wrapper.element = null
expect(wrapper.attributes()).to.eql({})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@

import { describeWithShallowAndMount } from '~resources/test-utils'
import { compileToFunctions } from 'vue-template-compiler'
import { mount } from '~vue-test-utils'
import ComponentWithCssModules from '~resources/components/component-with-css-modules.vue'

describe('classes', () => {
describeWithShallowAndMount('classes', (mountingMethod) => {
it('returns array of class names if wrapper has class names', () => {
const compiled = compileToFunctions('<div class="a-class b-class" />')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.classes()).to.contain('a-class')
expect(wrapper.classes()).to.contain('b-class')
})

it('returns empty array if wrapper has no classes', () => {
const compiled = compileToFunctions('<div />')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.classes().length).to.equal(0)
})

it('returns original class names when element mapped in css modules', () => {
const wrapper = mount(ComponentWithCssModules)
const wrapper = mountingMethod(ComponentWithCssModules)
expect(wrapper.classes()).to.eql(['extension', 'color-red'])
})
})
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { compileToFunctions } from 'vue-template-compiler'
import { mount } from '~vue-test-utils'
import ComponentWithChild from '~resources/components/component-with-child.vue'
import Component from '~resources/components/component.vue'
import FunctionalComponent from '~resources/components/functional-component.vue'
import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
import { functionalSFCsSupported } from '~resources/test-utils'
import {
functionalSFCsSupported,
describeWithShallowAndMount,
itSkipIf
} from '~resources/test-utils'
import ComponentWithoutName from '~resources/components/component-without-name.vue'

describe('contains', () => {
describeWithShallowAndMount('contains', (mountingMethod) => {
it('returns true if wrapper contains element', () => {
const compiled = compileToFunctions('<div><input /></div>')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.contains('input')).to.equal(true)
})

it('returns true if wrapper contains Vue component', () => {
const wrapper = mount(ComponentWithChild)
const wrapper = mountingMethod(ComponentWithChild)
expect(wrapper.contains(Component)).to.equal(true)
})

Expand All @@ -33,11 +36,13 @@ describe('contains', () => {
FunctionalComponent
}
}
const wrapper = mount(TestComponent)

const wrapper = mountingMethod(TestComponent)
expect(wrapper.contains(FunctionalComponent)).to.equal(true)
})

it('returns true if wrapper contains Vue class component', () => {
itSkipIf(mountingMethod.name === 'shallow',
'returns true if wrapper contains Vue class component', () => {
const TestComponent = {
template: `
<div>
Expand All @@ -48,19 +53,19 @@ describe('contains', () => {
ComponentAsAClass
}
}
const wrapper = mount(TestComponent)
const wrapper = mountingMethod(TestComponent)
expect(wrapper.contains(ComponentAsAClass)).to.equal(true)
})

it('returns true if wrapper contains element specified by ref selector', () => {
const compiled = compileToFunctions('<div><input ref="foo" /></div>')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.contains({ ref: 'foo' })).to.equal(true)
})

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

it('returns true when wrapper contains root element', () => {
const compiled = compileToFunctions('<div><input /></div>')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.contains('doesntexist')).to.equal(false)
})

it('returns true if wrapper root element matches contains', () => {
const compiled = compileToFunctions('<div><input /></div>')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.contains('doesntexist')).to.equal(false)
})

Expand All @@ -90,24 +95,24 @@ describe('contains', () => {
ComponentWithoutName
}
}
const wrapper = mount(TestComponent)
const wrapper = mountingMethod(TestComponent)
expect(wrapper.contains(ComponentWithoutName)).to.equal(true)
})

it('returns true if wrapper root Component matches selector', () => {
const wrapper = mount(Component)
const wrapper = mountingMethod(Component)
expect(wrapper.contains(Component)).to.equal(true)
})

it('returns false if wrapper does not contain element', () => {
const compiled = compileToFunctions('<div></div>')
const wrapper = mount(compiled)
const wrapper = mountingMethod(compiled)
expect(wrapper.contains('div')).to.equal(true)
})

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

Expand All @@ -121,13 +126,13 @@ describe('contains', () => {
})
}
}
const wrapper = mount(TestComponent)
const wrapper = mountingMethod(TestComponent)
expect(wrapper.contains('svg')).to.equal(true)
expect(wrapper.find('svg').contains('svg')).to.equal(true)
})

it('throws an error if selector is not a valid selector', () => {
const wrapper = mount(Component)
const wrapper = mountingMethod(Component)
const invalidSelectors = [
undefined, null, NaN, 0, 2, true, false, () => {}, {}, { name: undefined }, { ref: 'foo', nope: true }, []
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { compileToFunctions } from 'vue-template-compiler'
import { mount } from '~vue-test-utils'
import { describeWithShallowAndMount } from '~resources/test-utils'
import sinon from 'sinon'

describe('destroy', () => {
describeWithShallowAndMount('destroy', (mountingMethod) => {
it('should trigger beforeDestroy ', () => {
const spy = sinon.stub()
mount({
mountingMethod({
render: null,
beforeDestroy () {
spy()
Expand All @@ -16,7 +16,7 @@ describe('destroy', () => {

it('should trigger destroy ', () => {
const spy = sinon.stub()
mount({
mountingMethod({
render: null,
destroyed () {
spy()
Expand All @@ -27,7 +27,7 @@ describe('destroy', () => {

it('should remove element from document.body', () => {
const compiled = compileToFunctions('<div></div>')
const wrapper = mount(compiled, { attachToDocument: true })
const wrapper = mountingMethod(compiled, { attachToDocument: true })
expect(wrapper.vm.$el.parentNode).to.equal(document.body)
wrapper.destroy()
expect(wrapper.vm.$el.parentNode).to.be.null
Expand Down
Loading

0 comments on commit 20465ab

Please sign in to comment.