diff --git a/packages/shared/util.js b/packages/shared/util.js index ad5a163aa..dfb6a1c39 100644 --- a/packages/shared/util.js +++ b/packages/shared/util.js @@ -27,3 +27,26 @@ const hyphenateRE = /\B([A-Z])/g export const hyphenate = (str: string) => str.replace(hyphenateRE, '-$1').toLowerCase() export const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`) + +export const htmlTags = [ + 'html', 'body', 'base', 'head', 'link', 'meta', 'style', 'title', + 'address', 'article', 'aside', 'footer', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'hgroup', 'nav', 'section', 'div', 'dd', 'dl', 'dt', 'figcaption', 'figure', 'picture', 'hr', + 'img', 'li', 'main', 'ol', 'p', 'pre', 'ul', + 'a', 'b', 'abbr', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn', + 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', 'rtc', 'ruby', + 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', + 'u', 'var', 'wbr', 'area', 'audio', 'map', 'track', 'video', + 'embed', 'object', 'param', 'source', 'canvas', 'script', + 'noscript', 'del', 'ins', 'caption', 'col', 'colgroup', 'table', + 'thead', 'tbody', 'td', 'th', 'tr', 'button', 'datalist', 'fieldset', + 'form', 'input', 'label', 'legend', 'meter', 'optgroup', 'option', + 'output', 'progress', 'select', 'textarea', 'details', 'dialog', 'menu', + 'menuitem', 'summary', 'content', 'element', 'shadow', 'template', 'blockquote', 'iframe', 'tfoot' +] + +export const svgElements = [ + 'svg', 'animate', 'circle', 'clippath', 'cursor', 'defs', 'desc', 'ellipse', 'filter', 'font-face', + 'foreignObject', 'g', 'glyph', 'image', 'line', 'marker', 'mask', 'missing-glyph', 'path', 'pattern', + 'polygon', 'polyline', 'rect', 'switch', 'symbol', 'text', 'textpath', 'tspan', 'use', 'view' +] diff --git a/packages/shared/validators.js b/packages/shared/validators.js index bfecebca4..5e94aa53e 100644 --- a/packages/shared/validators.js +++ b/packages/shared/validators.js @@ -3,7 +3,9 @@ import { throwError, capitalize, camelize, - hyphenate + hyphenate, + htmlTags, + svgElements } from './util' export function isDomSelector (selector: any) { @@ -27,6 +29,22 @@ export function isDomSelector (selector: any) { } } +export function isTagSelector (selector: any) { + if (typeof selector !== 'string') { + return false + } + + const pseudoSelectors = ['.', '#', '[', ':', '>', ' '] + + if (htmlTags.includes(selector) || + svgElements.includes(selector) || + selector.split('').some(char => pseudoSelectors.includes(char))) { + return false + } else { + return true + } +} + export function isVueComponent (component: any) { if (typeof component === 'function' && component.options) { return true diff --git a/packages/test-utils/src/consts.js b/packages/test-utils/src/consts.js index f72395951..3e09299ed 100644 --- a/packages/test-utils/src/consts.js +++ b/packages/test-utils/src/consts.js @@ -4,5 +4,6 @@ export const NAME_SELECTOR = 'NAME_SELECTOR' export const COMPONENT_SELECTOR = 'COMPONENT_SELECTOR' export const REF_SELECTOR = 'REF_SELECTOR' export const DOM_SELECTOR = 'DOM_SELECTOR' +export const TAG_SELECTOR = 'TAG_SELECTOR' export const VUE_VERSION = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`) export const FUNCTIONAL_OPTIONS = VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions' diff --git a/packages/test-utils/src/find-vue-components.js b/packages/test-utils/src/find-vue-components.js index 28e73e298..914a45310 100644 --- a/packages/test-utils/src/find-vue-components.js +++ b/packages/test-utils/src/find-vue-components.js @@ -50,7 +50,18 @@ function findAllFunctionalComponentsFromVnode ( return components } +export function vmCtorMatchesTag (vm: Component, tag: string): boolean { + if (vm.$vnode.componentOptions.tag === tag) { + return true + } + + return false +} + export function vmCtorMatchesName (vm: Component, name: string): boolean { + if (!name) { + return false + } return !!((vm.$vnode && vm.$vnode.componentOptions && vm.$vnode.componentOptions.Ctor.options.name === name) || (vm._vnode && @@ -102,14 +113,22 @@ export default function findVueComponents ( node[FUNCTIONAL_OPTIONS].name === selector.name ) } - const nameSelector = typeof selector === 'function' ? selector.options.name : selector.name + + const nameSelector = typeof selector === 'function' + ? selector.options.name + : typeof selector === 'object' ? selector.name : selector + const components = root._isVue ? findAllVueComponentsFromVm(root) : findAllVueComponentsFromVnode(root) + return components.filter((component) => { if (!component.$vnode && !component.$options.extends) { return false } - return vmCtorMatchesSelector(component, selector) || vmCtorMatchesName(component, nameSelector) + + return vmCtorMatchesSelector(component, selector) || + vmCtorMatchesName(component, nameSelector) || + vmCtorMatchesTag(component, nameSelector) }) } diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 7b178b649..2f8cb5ffc 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -6,7 +6,8 @@ import findDOMNodes from './find-dom-nodes' import { COMPONENT_SELECTOR, NAME_SELECTOR, - DOM_SELECTOR + DOM_SELECTOR, + TAG_SELECTOR } from './consts' import Vue from 'vue' import getSelectorTypeOrThrow from './get-selector-type' @@ -24,7 +25,7 @@ export default function find ( throwError('cannot find a Vue instance on a DOM node. The node you are calling find on does not exist in the VDom. Are you adding the node as innerHTML?') } - if (selectorType === COMPONENT_SELECTOR || selectorType === NAME_SELECTOR) { + if (selectorType === COMPONENT_SELECTOR || selectorType === NAME_SELECTOR || selectorType === TAG_SELECTOR) { const root = vm || vnode if (!root) { return [] diff --git a/packages/test-utils/src/get-selector-type.js b/packages/test-utils/src/get-selector-type.js index 898c403ff..95aae6c7b 100644 --- a/packages/test-utils/src/get-selector-type.js +++ b/packages/test-utils/src/get-selector-type.js @@ -4,6 +4,7 @@ import { isDomSelector, isNameSelector, isRefSelector, + isTagSelector, isVueComponent } from 'shared/validators' import { @@ -13,10 +14,12 @@ import { REF_SELECTOR, COMPONENT_SELECTOR, NAME_SELECTOR, - DOM_SELECTOR + DOM_SELECTOR, + TAG_SELECTOR } from './consts' export default function getSelectorTypeOrThrow (selector: Selector, methodName: string): string | void { + if (isTagSelector(selector)) return TAG_SELECTOR if (isDomSelector(selector)) return DOM_SELECTOR if (isNameSelector(selector)) return NAME_SELECTOR if (isVueComponent(selector)) return COMPONENT_SELECTOR diff --git a/test/specs/wrapper/contains.spec.js b/test/specs/wrapper/contains.spec.js index 1abb600b3..ed671f9e9 100644 --- a/test/specs/wrapper/contains.spec.js +++ b/test/specs/wrapper/contains.spec.js @@ -74,13 +74,7 @@ describeWithShallowAndMount('contains', (mountingMethod) => { expect(fn).to.throw().with.property('message', message) }) - it('returns true when wrapper contains root element', () => { - const compiled = compileToFunctions('
') - const wrapper = mountingMethod(compiled) - expect(wrapper.contains('doesntexist')).to.equal(false) - }) - - it('returns true if wrapper root element matches contains', () => { + it('returns false when wrapper does not contain the element', () => { const compiled = compileToFunctions('
') const wrapper = mountingMethod(compiled) expect(wrapper.contains('doesntexist')).to.equal(false) diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index a4c7c281e..0a8432ca6 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -31,6 +31,11 @@ describeWithShallowAndMount('find', (mountingMethod) => { expect(wrapper.find('.foo').vnode).to.be.an('object') }) + it('returns Wrapper matching component tag passed', () => { + const wrapper = mountingMethod(ComponentWithChild) + expect(wrapper.find('child-component').vnode).to.be.an('object') + }) + it('returns Wrapper matching class selector passed if nested in a transition', () => { const compiled = compileToFunctions('
') const wrapper = mountingMethod(compiled)