-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
findElementStrategy.js
146 lines (117 loc) · 5.28 KB
/
findElementStrategy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { ProtocolError } from '../utils/ErrorHandler'
const DEFAULT_SELECTOR = 'css selector'
const DIRECT_SELECTOR_REGEXP = /^(id|css selector|xpath|link text|partial link text|name|tag name|class name|-android uiautomator|-ios uiautomation|accessibility id):(.+)/
let findStrategy = function (...args) {
let value = args[0]
let relative = (args.length > 1 ? args[1] : false)
let xpathPrefix = relative ? './/' : '//'
/**
* set default selector
*/
let using = DEFAULT_SELECTOR
if (typeof value !== 'string') {
throw new ProtocolError('selector needs to be typeof `string`')
}
if (args.length === 3) {
return {
using: args[0],
value: args[1]
}
}
/**
* check if user has specified locator strategy directly
*/
const match = value.match(DIRECT_SELECTOR_REGEXP)
if (match) {
return {
using: match[1],
value: match[2]
}
}
// check value type
// use id strategy if value starts with # and doesnt contain any other CSS selector-relevant character
// regex to match ids from http://stackoverflow.com/questions/18938390/regex-to-match-ids-in-a-css-file
if (value.search(/^#-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/) > -1) {
using = 'id'
value = value.slice(1)
// use xPath strategy if value starts with //
} else if (value.indexOf('/') === 0 || value.indexOf('(') === 0 ||
value.indexOf('../') === 0 || value.indexOf('./') === 0 ||
value.indexOf('*/') === 0) {
using = 'xpath'
// use link text startegy if value startes with =
} else if (value.indexOf('=') === 0) {
using = 'link text'
value = value.slice(1)
// use partial link text startegy if value startes with *=
} else if (value.indexOf('*=') === 0) {
using = 'partial link text'
value = value.slice(2)
// recursive element search using the UiAutomator library (Android only)
} else if (value.indexOf('android=') === 0) {
using = '-android uiautomator'
value = value.slice(8)
// recursive element search using the UIAutomation library (iOS-only)
} else if (value.indexOf('ios=') === 0) {
using = '-ios uiautomation'
value = value.slice(4)
// recursive element search using accessibility id
} else if (value.indexOf('~') === 0) {
using = 'accessibility id'
value = value.slice(1)
// class name mobile selector
// for iOS = UIA...
// for Android = android.widget
} else if (value.slice(0, 3) === 'UIA' || value.slice(0, 14).toLowerCase() === 'android.widget') {
using = 'class name'
// use tag name strategy if value contains a tag
// e.g. "<div>" or "<div />"
} else if (value.search(/<[a-zA-Z-]+( \/)*>/g) >= 0) {
using = 'tag name'
value = value.replace(/<|>|\/|\s/g, '')
// use name strategy if value queries elements with name attributes
// e.g. "[name='myName']" or '[name="myName"]'
} else if (value.search(/^\[name=("|')([a-zA-z0-9\-_. ]+)("|')]$/) >= 0) {
using = 'name'
value = value.match(/^\[name=("|')([a-zA-z0-9\-_. ]+)("|')]$/)[2]
// any element with given text e.g. h1=Welcome
} else if (value.search(/^[a-z0-9]*=(.)+$/) >= 0) {
let query = value.split(/=/)
let tag = query.shift()
using = 'xpath'
value = `${xpathPrefix}${tag.length ? tag : '*'}[normalize-space() = "${query.join('=')}"]`
// any element containing given text
} else if (value.search(/^[a-z0-9]*\*=(.)+$/) >= 0) {
let query = value.split(/\*=/)
let tag = query.shift()
using = 'xpath'
value = `${xpathPrefix}${tag.length ? tag : '*'}[contains(., "${query.join('*=')}")]`
// any element with certian class or id + given content
} else if (value.search(/^[a-z0-9]*(\.|#)-?[_a-zA-Z]+[_a-zA-Z0-9-]*=(.)+$/) >= 0) {
let query = value.split(/=/)
let tag = query.shift()
let classOrId = tag.substr(tag.search(/(\.|#)/), 1) === '#' ? 'id' : 'class'
let classOrIdName = tag.slice(tag.search(/(\.|#)/) + 1)
tag = tag.substr(0, tag.search(/(\.|#)/))
using = 'xpath'
value = `${xpathPrefix}${tag.length ? tag : '*'}[contains(@${classOrId}, "${classOrIdName}") and normalize-space() = "${query.join('=')}"]`
// any element with certian class or id + has certain content
} else if (value.search(/^[a-z0-9]*(\.|#)-?[_a-zA-Z]+[_a-zA-Z0-9-]*\*=(.)+$/) >= 0) {
let query = value.split(/\*=/)
let tag = query.shift()
let classOrId = tag.substr(tag.search(/(\.|#)/), 1) === '#' ? 'id' : 'class'
let classOrIdName = tag.slice(tag.search(/(\.|#)/) + 1)
tag = tag.substr(0, tag.search(/(\.|#)/))
using = 'xpath'
value = xpathPrefix + (tag.length ? tag : '*') + '[contains(@' + classOrId + ', "' + classOrIdName + '") and contains(., "' + query.join('*=') + '")]'
value = `${xpathPrefix}${tag.length ? tag : '*'}[contains(@${classOrId}, "${classOrIdName}") and contains(., "${query.join('*=')}")]`
// allow to move up to the parent or select current element
} else if (value === '..' || value === '.') {
using = 'xpath'
}
return {
using: using,
value: value
}
}
export default findStrategy