Skip to content

Commit 7db74a3

Browse files
committed
refactor: extract find logic to find module
1 parent 4354d62 commit 7db74a3

File tree

7 files changed

+150
-142
lines changed

7 files changed

+150
-142
lines changed

src/lib/find-vnodes-by-ref.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/lib/find-vnodes-by-selector.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/lib/find-vnodes.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// @flow
2+
3+
import {
4+
REF_SELECTOR
5+
} from './consts'
6+
import {
7+
throwError
8+
} from './util'
9+
10+
function findAllVNodes (vnode: VNode, nodes: Array<VNode> = []): Array<VNode> {
11+
nodes.push(vnode)
12+
13+
if (Array.isArray(vnode.children)) {
14+
vnode.children.forEach((childVNode) => {
15+
findAllVNodes(childVNode, nodes)
16+
})
17+
}
18+
19+
if (vnode.child) {
20+
findAllVNodes(vnode.child._vnode, nodes)
21+
}
22+
23+
return nodes
24+
}
25+
26+
function removeDuplicateNodes (vNodes: Array<VNode>): Array<VNode> {
27+
const uniqueNodes = []
28+
vNodes.forEach((vNode) => {
29+
const exists = uniqueNodes.some(node => vNode.elm === node.elm)
30+
if (!exists) {
31+
uniqueNodes.push(vNode)
32+
}
33+
})
34+
return uniqueNodes
35+
}
36+
37+
function nodeMatchesRef (node: VNode, refName: string): boolean {
38+
return node.data && node.data.ref === refName
39+
}
40+
41+
function findVNodesByRef (vNode: VNode, refName: string): Array<VNode> {
42+
const nodes = findAllVNodes(vNode)
43+
const refFilteredNodes = nodes.filter(node => nodeMatchesRef(node, refName))
44+
// Only return refs defined on top-level VNode to provide the same
45+
// behavior as selecting via vm.$ref.{someRefName}
46+
const mainVNodeFilteredNodes = refFilteredNodes.filter(node => (
47+
!!vNode.context.$refs[node.data.ref]
48+
))
49+
return removeDuplicateNodes(mainVNodeFilteredNodes)
50+
}
51+
52+
function nodeMatchesSelector (node: VNode, selector: string): boolean {
53+
return node.elm && node.elm.getAttribute && node.elm.matches(selector)
54+
}
55+
56+
function findVNodesBySelector (
57+
vNode: VNode,
58+
selector: string
59+
): Array<VNode> {
60+
const nodes = findAllVNodes(vNode)
61+
const filteredNodes = nodes.filter(node => (
62+
nodeMatchesSelector(node, selector)
63+
))
64+
return removeDuplicateNodes(filteredNodes)
65+
}
66+
67+
export default function findVnodes (
68+
vnode: VNode,
69+
vm: Component | null,
70+
selectorType: ?string,
71+
selector: Object | string
72+
): Array<VNode> {
73+
if (selectorType === REF_SELECTOR) {
74+
if (!vm) {
75+
throwError('$ref selectors can only be used on Vue component wrappers')
76+
}
77+
// $FlowIgnore
78+
return findVNodesByRef(vnode, selector.ref)
79+
}
80+
// $FlowIgnore
81+
return findVNodesBySelector(vnode, selector)
82+
}

src/lib/find-vue-components.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import {
33
COMPONENT_SELECTOR
44
} from './consts'
55

6-
function findAllVueComponentsFromVm (vm: Component, components: Array<Component> = []): Array<Component> {
6+
function findAllVueComponentsFromVm (
7+
vm: Component,
8+
components: Array<Component> = []
9+
): Array<Component> {
710
components.push(vm)
811
vm.$children.forEach((child) => {
912
findAllVueComponentsFromVm(child, components)
@@ -12,7 +15,10 @@ function findAllVueComponentsFromVm (vm: Component, components: Array<Component>
1215
return components
1316
}
1417

15-
function findAllVueComponentsFromVnode (vnode: Component, components: Array<Component> = []): Array<Component> {
18+
function findAllVueComponentsFromVnode (
19+
vnode: Component,
20+
components: Array<Component> = []
21+
): Array<Component> {
1622
if (vnode.child) {
1723
components.push(vnode.child)
1824
}
@@ -26,8 +32,10 @@ function findAllVueComponentsFromVnode (vnode: Component, components: Array<Comp
2632
}
2733

2834
export function vmCtorMatchesName (vm: Component, name: string): boolean {
29-
return (vm.$vnode && vm.$vnode.componentOptions && vm.$vnode.componentOptions.Ctor.options.name === name) ||
30-
(vm._vnode && vm._vnode.functionalOptions && vm._vnode.functionalOptions.name === name) ||
35+
return (vm.$vnode && vm.$vnode.componentOptions &&
36+
vm.$vnode.componentOptions.Ctor.options.name === name) ||
37+
(vm._vnode && vm._vnode.functionalOptions &&
38+
vm._vnode.functionalOptions.name === name) ||
3139
vm.$options && vm.$options.name === name
3240
}
3341

@@ -36,8 +44,14 @@ export function vmCtorMatchesSelector (component: Component, Ctor: Object) {
3644
return Ctors.some(c => Ctor[c] === component.__proto__.constructor)
3745
}
3846

39-
export default function findVueComponents (root: Component, selectorType: string, selector: Object): Array<Component> {
40-
const components = root._isVue ? findAllVueComponentsFromVm(root) : findAllVueComponentsFromVnode(root)
47+
export default function findVueComponents (
48+
root: Component,
49+
selectorType: ?string,
50+
selector: Object
51+
): Array<Component> {
52+
const components = root._isVue
53+
? findAllVueComponentsFromVm(root)
54+
: findAllVueComponentsFromVnode(root)
4155
return components.filter((component) => {
4256
if (!component.$vnode && !component.$options.extends) {
4357
return false

src/lib/find.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @flow
2+
3+
import findVnodes from './find-vnodes'
4+
import findVueComponents from './find-vue-components'
5+
import {
6+
COMPONENT_SELECTOR,
7+
NAME_SELECTOR
8+
} from './consts'
9+
import Vue from 'vue'
10+
11+
export default function find (
12+
vm: Component | null,
13+
vnode: VNode,
14+
selectorType: ?string,
15+
selector: Selector
16+
): Array<VNode | Component> {
17+
if (selectorType === COMPONENT_SELECTOR || selectorType === NAME_SELECTOR) {
18+
const root = vm || vnode
19+
return findVueComponents(root, selectorType, selector)
20+
}
21+
22+
if (vm && vm.$refs && selector.ref in vm.$refs && vm.$refs[selector.ref] instanceof Vue) {
23+
return [vm.$refs[selector.ref]]
24+
}
25+
26+
return findVnodes(vnode, vm, selectorType, selector)
27+
}

src/lib/vnode-utils.js

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/wrappers/wrapper.js

Lines changed: 21 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ import getSelectorTypeOrThrow from '../lib/get-selector-type'
55
import {
66
REF_SELECTOR,
77
COMPONENT_SELECTOR,
8-
NAME_SELECTOR,
9-
DOM_SELECTOR
8+
NAME_SELECTOR
109
} from '../lib/consts'
11-
import findVueComponents, { vmCtorMatchesName, vmCtorMatchesSelector } from '../lib/find-vue-components'
12-
import findVNodesBySelector from '../lib/find-vnodes-by-selector'
13-
import findVNodesByRef from '../lib/find-vnodes-by-ref'
10+
import {
11+
vmCtorMatchesName,
12+
vmCtorMatchesSelector
13+
} from '../lib/find-vue-components'
1414
import VueWrapper from './vue-wrapper'
1515
import WrapperArray from './wrapper-array'
1616
import ErrorWrapper from './error-wrapper'
1717
import { throwError, warn } from '../lib/util'
18+
import findAll from '../lib/find'
1819

1920
export default class Wrapper implements BaseWrapper {
2021
vnode: VNode;
@@ -78,25 +79,9 @@ export default class Wrapper implements BaseWrapper {
7879
*/
7980
contains (selector: Selector) {
8081
const selectorType = getSelectorTypeOrThrow(selector, 'contains')
81-
82-
if (selectorType === NAME_SELECTOR || selectorType === COMPONENT_SELECTOR) {
83-
const vm = this.vm || this.vnode.context.$root
84-
return findVueComponents(vm, selector, selector).length > 0 || this.is(selector)
85-
}
86-
87-
if (selectorType === REF_SELECTOR) {
88-
if (!this.vm) {
89-
throwError('$ref selectors can only be used on Vue component wrappers')
90-
}
91-
const nodes = findVNodesByRef(this.vnode, selector.ref)
92-
return nodes.length > 0
93-
}
94-
95-
if (selectorType === DOM_SELECTOR && this.element instanceof HTMLElement) {
96-
return this.element.querySelectorAll(selector).length > 0 || this.is(selector)
97-
}
98-
99-
return false
82+
const nodes = findAll(this.vm, this.vnode, selectorType, selector)
83+
const is = selectorType === REF_SELECTOR ? false : this.is(selector)
84+
return nodes.length > 0 || is
10085
}
10186

10287
/**
@@ -234,73 +219,29 @@ export default class Wrapper implements BaseWrapper {
234219
*/
235220
find (selector: Selector): Wrapper | ErrorWrapper | VueWrapper {
236221
const selectorType = getSelectorTypeOrThrow(selector, 'find')
237-
238-
if (selectorType === COMPONENT_SELECTOR ||
239-
selectorType === NAME_SELECTOR) {
240-
const root = this.vm || this.vnode
241-
// $FlowIgnore warning about selectorType being undefined
242-
const components = findVueComponents(root, selectorType, selector)
243-
if (components.length === 0) {
244-
return new ErrorWrapper('Component')
245-
}
246-
return new VueWrapper(components[0], this.options)
247-
}
248-
249-
if (selectorType === REF_SELECTOR) {
250-
if (!this.vm) {
251-
throwError('$ref selectors can only be used on Vue component wrappers')
252-
}
253-
if (this.vm && this.vm.$refs && selector.ref in this.vm.$refs && this.vm.$refs[selector.ref] instanceof Vue) {
254-
return new VueWrapper(this.vm.$refs[selector.ref], this.options)
255-
}
256-
const nodes = findVNodesByRef(this.vnode, selector.ref)
257-
if (nodes.length === 0) {
222+
const nodes = findAll(this.vm, this.vnode, selectorType, selector)
223+
if (nodes.length === 0) {
224+
if (selector.ref) {
258225
return new ErrorWrapper(`ref="${selector.ref}"`)
259226
}
260-
return new Wrapper(nodes[0], this.update, this.options)
227+
return new ErrorWrapper(typeof selector === 'string' ? selector : 'Component')
261228
}
262-
263-
const nodes = findVNodesBySelector(this.vnode, selector)
264-
265-
if (nodes.length === 0) {
266-
return new ErrorWrapper(selector)
267-
}
268-
return new Wrapper(nodes[0], this.update, this.options)
229+
return nodes[0] instanceof Vue
230+
? new VueWrapper(nodes[0], this.options)
231+
: new Wrapper(nodes[0], this.update, this.options)
269232
}
270233

271234
/**
272235
* Finds node in tree of the current wrapper that matches the provided selector.
273236
*/
274237
findAll (selector: Selector): WrapperArray {
275238
const selectorType = getSelectorTypeOrThrow(selector, 'findAll')
239+
const nodes = findAll(this.vm, this.vnode, selectorType, selector)
240+
const wrappers = nodes[0] && nodes[0] instanceof Vue
241+
? nodes.map(node => new VueWrapper(node, this.options))
242+
: nodes.map(node => new Wrapper(node, this.update, this.options))
276243

277-
if (selectorType === COMPONENT_SELECTOR ||
278-
selectorType === NAME_SELECTOR) {
279-
const root = this.vm || this.vnode
280-
// $FlowIgnore warning about selectorType being undefined
281-
const components = findVueComponents(root, selectorType, selector)
282-
return new WrapperArray(components.map(component => new VueWrapper(component, this.options)))
283-
}
284-
285-
if (selectorType === REF_SELECTOR) {
286-
if (!this.vm) {
287-
throwError('$ref selectors can only be used on Vue component wrappers')
288-
}
289-
if (this.vm && this.vm.$refs && selector.ref in this.vm.$refs && this.vm.$refs[selector.ref] instanceof Vue) {
290-
return new WrapperArray([new VueWrapper(this.vm.$refs[selector.ref], this.options)])
291-
}
292-
const nodes = findVNodesByRef(this.vnode, selector.ref)
293-
return new WrapperArray(nodes.map(node => new Wrapper(node, this.update, this.options)))
294-
}
295-
296-
function nodeMatchesSelector (node, selector) {
297-
return node.elm && node.elm.getAttribute && node.elm.matches(selector)
298-
}
299-
300-
const nodes = findVNodesBySelector(this.vnode, selector)
301-
const matchingNodes = nodes.filter(node => nodeMatchesSelector(node, selector))
302-
303-
return new WrapperArray(matchingNodes.map(node => new Wrapper(node, this.update, this.options)))
244+
return new WrapperArray(wrappers)
304245
}
305246

306247
/**

0 commit comments

Comments
 (0)