testa_appium_driver 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +15 -15
- data/.idea/deployment.xml +21 -21
- data/.idea/inspectionProfiles/Project_Default.xml +8 -8
- data/.idea/misc.xml +5 -5
- data/.idea/modules.xml +7 -7
- data/.idea/runConfigurations/Android_Test.xml +41 -41
- data/.idea/runConfigurations.xml +9 -9
- data/.idea/sshConfigs.xml +12 -12
- data/.idea/vcs.xml +5 -5
- data/.idea/webServers.xml +20 -20
- data/.rspec +3 -3
- data/.rubocop.yml +13 -13
- data/CHANGELOG.md +5 -5
- data/CODE_OF_CONDUCT.md +102 -102
- data/Gemfile +12 -12
- data/LICENSE.txt +21 -21
- data/README.md +378 -378
- data/Rakefile +12 -12
- data/bin/console +17 -17
- data/bin/setup +8 -8
- data/lib/testa_appium_driver/android/class_selectors.rb +437 -437
- data/lib/testa_appium_driver/android/driver.rb +69 -69
- data/lib/testa_appium_driver/android/locator/attributes.rb +113 -113
- data/lib/testa_appium_driver/android/locator.rb +141 -141
- data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +61 -61
- data/lib/testa_appium_driver/android/selenium_element.rb +7 -7
- data/lib/testa_appium_driver/common/bounds.rb +149 -149
- data/lib/testa_appium_driver/common/constants.rb +36 -36
- data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +11 -11
- data/lib/testa_appium_driver/common/helpers.rb +270 -270
- data/lib/testa_appium_driver/common/locator/scroll_actions.rb +397 -397
- data/lib/testa_appium_driver/common/locator.rb +610 -610
- data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +3 -3
- data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +237 -237
- data/lib/testa_appium_driver/common/scroll_actions.rb +246 -246
- data/lib/testa_appium_driver/common/selenium_element.rb +19 -19
- data/lib/testa_appium_driver/driver.rb +312 -312
- data/lib/testa_appium_driver/ios/driver.rb +48 -48
- data/lib/testa_appium_driver/ios/locator/attributes.rb +80 -80
- data/lib/testa_appium_driver/ios/locator.rb +70 -70
- data/lib/testa_appium_driver/ios/selenium_element.rb +6 -6
- data/lib/testa_appium_driver/ios/type_selectors.rb +187 -187
- data/lib/testa_appium_driver/version.rb +5 -5
- data/lib/testa_appium_driver.rb +6 -6
- data/testa_appium_driver.gemspec +41 -41
- data/testa_appium_driver.iml +27 -78
- metadata +3 -3
@@ -1,271 +1,271 @@
|
|
1
|
-
module TestaAppiumDriver
|
2
|
-
module Helpers
|
3
|
-
|
4
|
-
# supported selectors
|
5
|
-
# id: "com.my.package:id/myId"
|
6
|
-
# id: "myId" => will be converted to "com.my.package:id/myId"
|
7
|
-
# id: /my/ will find all elements with ids that contain my
|
8
|
-
# desc: "element description"
|
9
|
-
# desc: /ription/ will find all elements that contains ription
|
10
|
-
# class: "android.widget.Button"
|
11
|
-
# class: /Button/ will find all elements with classes that contain Button
|
12
|
-
# text: "Hello world"
|
13
|
-
# text: /ello/ will find all elements with text that contain ello
|
14
|
-
# package: "com.my.package"
|
15
|
-
# package: /my/ will find all elements with package that contains my
|
16
|
-
# long_clickable: true or false
|
17
|
-
# checkable: true or false
|
18
|
-
# checked: true or false
|
19
|
-
# clickable: true or false
|
20
|
-
# enabled: true or false
|
21
|
-
# focusable: true or false
|
22
|
-
# focused: true or false
|
23
|
-
# index: child index inside of a parent element, index starts from 0
|
24
|
-
# selected: true or false
|
25
|
-
# scrollable: true or false
|
26
|
-
# @param [Hash] hash selectors for finding elements
|
27
|
-
# @param [Boolean] single should the command return first instance or all of matched elements
|
28
|
-
# @return [String] hash selectors converted to uiautomator command
|
29
|
-
def hash_to_uiautomator(hash, single = true)
|
30
|
-
command = "new UiSelector()"
|
31
|
-
|
32
|
-
id = resolve_id(hash[:id])
|
33
|
-
command = "#{ command }.resourceId(\"#{ %(#{ id }) }\")" if id && id.kind_of?(String)
|
34
|
-
command = "#{ command }.resourceIdMatches(\".*#{ %(#{ id.source }) }.*\")" if id && id.kind_of?(Regexp)
|
35
|
-
command = "#{ command }.description(\"#{ %(#{ hash[:desc] }) }\")" if hash[:desc] && hash[:desc].kind_of?(String)
|
36
|
-
command = "#{ command }.descriptionMatches(\".*#{ %(#{ hash[:desc].source }) }.*\")" if hash[:desc] && hash[:desc].kind_of?(Regexp)
|
37
|
-
command = "#{ command }.className(\"#{ %(#{ hash[:class] }) }\")" if hash[:class] && hash[:class].kind_of?(String)
|
38
|
-
command = "#{ command }.classNameMatches(\".*#{ %(#{ hash[:class].source }) }.*\")" if hash[:class] && hash[:class].kind_of?(Regexp)
|
39
|
-
command = "#{ command }.text(\"#{ %(#{ hash[:text] }) }\")" if hash[:text] && hash[:text].kind_of?(String)
|
40
|
-
command = "#{ command }.textMatches(\".*#{ %(#{ hash[:text].source }) }.*\")" if hash[:text] && hash[:text].kind_of?(Regexp)
|
41
|
-
command = "#{ command }.packageName(\"#{ %(#{ hash[:package] }) }\")" if hash[:package] && hash[:package].kind_of?(String)
|
42
|
-
command = "#{ command }.packageNameMatches(\".*#{ %(#{ hash[:package].source }) }.*\")" if hash[:package] && hash[:package].kind_of?(Regexp)
|
43
|
-
|
44
|
-
command = "#{ command }.longClickable(#{ hash[:long_clickable] })" if hash[:long_clickable]
|
45
|
-
command = "#{ command }.checkable(#{ hash[:checkable] })" unless hash[:checkable].nil?
|
46
|
-
command = "#{ command }.checked(#{ hash[:checked] })" unless hash[:checked].nil?
|
47
|
-
command = "#{ command }.clickable(#{ hash[:clickable] })" unless hash[:clickable].nil?
|
48
|
-
command = "#{ command }.enabled(#{ hash[:enabled] })" unless hash[:enabled].nil?
|
49
|
-
command = "#{ command }.focusable(#{ hash[:focusable] })" unless hash[:focusable].nil?
|
50
|
-
command = "#{ command }.focused(#{ hash[:focused] })" unless hash[:focused].nil?
|
51
|
-
command = "#{ command }.index(#{ hash[:index].to_s })" unless hash[:index].nil?
|
52
|
-
command = "#{ command }.selected(#{ hash[:selected] })" unless hash[:selected].nil?
|
53
|
-
command = "#{ command }.scrollable(#{ hash[:scrollable] })" unless hash[:scrollable].nil?
|
54
|
-
|
55
|
-
command += ".instance(0)" if single
|
56
|
-
|
57
|
-
command
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
# supported selectors
|
62
|
-
# id: "com.my.package:id/myId"
|
63
|
-
# id: "myId" => will be converted to "com.my.package:id/myId"
|
64
|
-
# id: /my/ will find all elements with ids that contain my
|
65
|
-
# desc: "element description"
|
66
|
-
# desc: /ription/ will find all elements that contains ription
|
67
|
-
# class: "android.widget.Button"
|
68
|
-
# class: /Button/ will find all elements with classes that contain Button
|
69
|
-
# text: "Hello world"
|
70
|
-
# text: /ello/ will find all elements with text that contain ello
|
71
|
-
# package: "com.my.package"
|
72
|
-
# package: /my/ will find all elements with package that contains my
|
73
|
-
# long_clickable: true or false
|
74
|
-
# checkable: true or false
|
75
|
-
# checked: true or false
|
76
|
-
# clickable: true or false
|
77
|
-
# enabled: true or false
|
78
|
-
# focusable: true or false
|
79
|
-
# focused: true or false
|
80
|
-
# index: child index inside of a parent element, index starts from 0
|
81
|
-
# selected: true or false
|
82
|
-
# scrollable: true or false
|
83
|
-
# @param [Hash] hash selectors for finding elements
|
84
|
-
# @param [Boolean] single should the command return first instance or all of matched elements
|
85
|
-
# @return [String] hash selectors converted to xpath command
|
86
|
-
def hash_to_xpath(device, hash, single = true)
|
87
|
-
for_android = device == :android
|
88
|
-
|
89
|
-
|
90
|
-
command = "//"
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
if for_android
|
95
|
-
id = resolve_id(hash[:id])
|
96
|
-
if hash[:class] && hash[:class].kind_of?(String)
|
97
|
-
command = "#{ command }#{hash[:class] }"
|
98
|
-
elsif hash[:class] && hash[:class].kind_of?(Regexp)
|
99
|
-
command = "#{ command}*[contains(@class, \"#{ %(#{hash[:class].source }) }\")]"
|
100
|
-
else
|
101
|
-
command = "#{command}*"
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
command = "#{ command }[@resource-id=\"#{ %(#{ id }) }\"]" if id && id.kind_of?(String)
|
106
|
-
command = "#{ command }[contains(@resource-id, \"#{ %(#{ id.source }) }\")]" if id && id.kind_of?(Regexp)
|
107
|
-
command = "#{ command }[@content-desc=\"#{ %(#{hash[:desc] }) }\"]" if hash[:desc] && hash[:desc].kind_of?(String)
|
108
|
-
command = "#{ command }[contains(@content-desc, \"#{ %(#{hash[:desc].source }) }\")]" if hash[:desc] && hash[:desc].kind_of?(Regexp)
|
109
|
-
command = "#{ command }[contains(@class, \"#{ %(#{hash[:class].source }) }\")]" if hash[:class] && hash[:class].kind_of?(Regexp)
|
110
|
-
command = "#{ command }[@text=\"#{ %(#{hash[:text] }) }\"]" if hash[:text] && hash[:text].kind_of?(String)
|
111
|
-
command = "#{ command }[contains(@text, \"#{ %(#{hash[:text].source }) }\")]" if hash[:text] && hash[:text].kind_of?(Regexp)
|
112
|
-
command = "#{ command }[@package=\"#{ %(#{hash[:package] }) }\"]" if hash[:package] && hash[:package].kind_of?(String)
|
113
|
-
command = "#{ command }[contains=(@package, \"#{ %(#{hash[:package].source }) }\")]" if hash[:package] && hash[:package].kind_of?(Regexp)
|
114
|
-
command = "#{ command }[@long-clickable=\"#{ hash[:long_clickable] }\"]" if hash[:long_clickable]
|
115
|
-
command = "#{ command }[@checkable=\"#{ hash[:checkable] }\"]" unless hash[:checkable].nil?
|
116
|
-
command = "#{ command }[@checked=\"#{ hash[:checked] }\"]" unless hash[:checked].nil?
|
117
|
-
command = "#{ command }[@clickable=\"#{ hash[:clickable] }\"]" unless hash[:clickable].nil?
|
118
|
-
command = "#{ command }[@enabled=\"#{ hash[:enabled] }\"]" unless hash[:enabled].nil?
|
119
|
-
command = "#{ command }[@focusable=\"#{ hash[:focusable] }\"]" unless hash[:focusable].nil?
|
120
|
-
command = "#{ command }[@focused=\"#{ hash[:focused] }\"]" unless hash[:focused].nil?
|
121
|
-
command = "#{ command }[@index=\"#{ hash[:index] }\"]" unless hash[:index].nil?
|
122
|
-
command = "#{ command }[@selected=\"#{ hash[:selected] }\"]" unless hash[:selected].nil?
|
123
|
-
|
124
|
-
# it seems like you cannot query by scrollable
|
125
|
-
# command = "#{ command }[@scrollable=\"#{ hash[:scrollable] }\"]" unless hash[:scrollable].nil?
|
126
|
-
else
|
127
|
-
|
128
|
-
hash[:type] = hash[:class] unless hash[:class].nil?
|
129
|
-
if hash[:type] && hash[:type].kind_of?(String)
|
130
|
-
command = "#{ command }#{hash[:type] }"
|
131
|
-
elsif hash[:type] && hash[:type].kind_of?(Regexp)
|
132
|
-
command = "#{ command}*[contains(@type, \"#{ %(#{hash[:type].source }) }\")]"
|
133
|
-
else
|
134
|
-
command = "#{command}*"
|
135
|
-
end
|
136
|
-
|
137
|
-
|
138
|
-
# # ios specific
|
139
|
-
hash[:label] = hash[:text] unless hash[:text].nil?
|
140
|
-
hash[:name] = hash[:id] unless hash[:id].nil?
|
141
|
-
|
142
|
-
command = "#{ command }[@enabled=\"#{ hash[:enabled] }\"]" unless hash[:enabled].nil?
|
143
|
-
command = "#{ command }[@label=\"#{ %(#{hash[:label] }) }\"]" if hash[:label] && hash[:label].kind_of?(String)
|
144
|
-
command = "#{ command }[contains(@label, \"#{ %(#{hash[:label].source }) }\")]" if hash[:label] && hash[:label].kind_of?(Regexp)
|
145
|
-
command = "#{ command }[@name=\"#{ %(#{hash[:name] }) }\"]" if hash[:name] && hash[:name].kind_of?(String)
|
146
|
-
command = "#{ command }[contains(@name, \"#{ %(#{hash[:name].source }) }\")]" if hash[:name] && hash[:name].kind_of?(Regexp)
|
147
|
-
command = "#{ command }[@value=\"#{ %(#{hash[:value] }) }\"]" if hash[:value] && hash[:value].kind_of?(String)
|
148
|
-
command = "#{ command }[contains(@value, \"#{ %(#{hash[:value].source }) }\")]" if hash[:value] && hash[:value].kind_of?(Regexp)
|
149
|
-
command = "#{ command }[@width=\"#{ hash[:width] }\"]" unless hash[:width].nil?
|
150
|
-
command = "#{ command }[@height=\"#{ hash[:height] }\"]" unless hash[:height].nil?
|
151
|
-
command = "#{ command }[@visible=\"#{ hash[:visible] }\"]" unless hash[:visible].nil?
|
152
|
-
end
|
153
|
-
|
154
|
-
|
155
|
-
command += "[1]" if single
|
156
|
-
|
157
|
-
command
|
158
|
-
end
|
159
|
-
|
160
|
-
|
161
|
-
def hash_to_class_chain(hash, single = true)
|
162
|
-
command = "**/"
|
163
|
-
|
164
|
-
hash[:type] = hash[:class] unless hash[:class].nil?
|
165
|
-
hash[:label] = hash[:text] unless hash[:text].nil?
|
166
|
-
hash[:name] = hash[:id] unless hash[:id].nil?
|
167
|
-
if hash[:type] && hash[:type].kind_of?(String)
|
168
|
-
command = "#{ command }#{hash[:type] }"
|
169
|
-
else
|
170
|
-
command = "#{command}*"
|
171
|
-
end
|
172
|
-
|
173
|
-
command = "#{ command }[`enabled == #{ hash[:enabled] }`]" unless hash[:enabled].nil?
|
174
|
-
command = "#{ command }[`label == \"#{ %(#{hash[:label] }) }\"`]" if hash[:label] && hash[:label].kind_of?(String)
|
175
|
-
command = "#{ command }[`label CONTAINS \"#{ %(#{hash[:label].source }) }\"`]" if hash[:label] && hash[:label].kind_of?(Regexp)
|
176
|
-
command = "#{ command }[`name == \"#{ %(#{hash[:name] }) }\"`]" if hash[:name] && hash[:name].kind_of?(String)
|
177
|
-
command = "#{ command }[`name CONTAINS \"#{ %(#{hash[:name].source }) }\"`]" if hash[:name] && hash[:name].kind_of?(Regexp)
|
178
|
-
command = "#{ command }[`value == \"#{ %(#{hash[:value] }) }\"`]" if hash[:value] && hash[:value].kind_of?(String)
|
179
|
-
command = "#{ command }[`value CONTAINS \"#{ %(#{hash[:value].source }) }\"`]" if hash[:value] && hash[:value].kind_of?(Regexp)
|
180
|
-
command = "#{ command }[`visible == #{ hash[:visible] }`]" unless hash[:visible].nil?
|
181
|
-
|
182
|
-
command += "[1]" if single
|
183
|
-
|
184
|
-
command
|
185
|
-
end
|
186
|
-
|
187
|
-
# check if selectors are for a scrollable element
|
188
|
-
# @param [Boolean] single should the command return first instance or all of matched elements
|
189
|
-
# @param [Hash] selectors for fetching elements
|
190
|
-
# @return [Boolean] true if element has scrollable attribute true or is class one of (RecyclerView, HorizontalScrollView, ScrollView, ListView)
|
191
|
-
def is_scrollable_selector?(selectors, single)
|
192
|
-
return false unless single
|
193
|
-
return true if selectors[:scrollable]
|
194
|
-
if selectors[:class] == "androidx.recyclerview.widget.RecyclerView" ||
|
195
|
-
selectors[:class] == "android.widget.HorizontalScrollView" ||
|
196
|
-
selectors[:class] == "android.widget.ScrollView" ||
|
197
|
-
selectors[:class] == "android.widget.ListView"
|
198
|
-
return true
|
199
|
-
elsif selectors[:type] == "XCUIElementTypeScrollView"
|
200
|
-
return true
|
201
|
-
end
|
202
|
-
false
|
203
|
-
end
|
204
|
-
|
205
|
-
#noinspection RubyUnnecessaryReturnStatement,RubyUnusedLocalVariable
|
206
|
-
# separate selectors from given hash parameters
|
207
|
-
# @param [Hash] params
|
208
|
-
# @return [Array] first element is params, second are selectors
|
209
|
-
def extract_selectors_from_params(params = {})
|
210
|
-
selectors = params.select { |key, value| [
|
211
|
-
:id,
|
212
|
-
:long_clickable,
|
213
|
-
:desc,
|
214
|
-
:class,
|
215
|
-
:text,
|
216
|
-
:package,
|
217
|
-
:checkable,
|
218
|
-
:checked,
|
219
|
-
:clickable,
|
220
|
-
:enabled,
|
221
|
-
:focusable,
|
222
|
-
:focused,
|
223
|
-
:index,
|
224
|
-
:selected,
|
225
|
-
:scrollable,
|
226
|
-
|
227
|
-
# ios specific
|
228
|
-
:type,
|
229
|
-
:label,
|
230
|
-
:x,
|
231
|
-
:y,
|
232
|
-
:width,
|
233
|
-
:height,
|
234
|
-
:visible,
|
235
|
-
:name,
|
236
|
-
:value,
|
237
|
-
|
238
|
-
:image
|
239
|
-
].include?(key) }
|
240
|
-
params = Hash[params.to_a - selectors.to_a]
|
241
|
-
|
242
|
-
# default params
|
243
|
-
params[:single] = true if params[:single].nil?
|
244
|
-
params[:scrollable_locator] = nil if params[:scrollable_locator].nil?
|
245
|
-
if params[:default_find_strategy].nil?
|
246
|
-
params[:default_find_strategy] = DEFAULT_ANDROID_FIND_STRATEGY if @driver.device == :android
|
247
|
-
params[:default_find_strategy] = DEFAULT_IOS_FIND_STRATEGY if @driver.device == :ios || @driver.device == :tvos
|
248
|
-
end
|
249
|
-
if params[:default_scroll_strategy].nil?
|
250
|
-
params[:default_scroll_strategy] = DEFAULT_ANDROID_SCROLL_STRATEGY if @driver.device == :android
|
251
|
-
params[:default_scroll_strategy] = DEFAULT_IOS_SCROLL_STRATEGY if @driver.device == :ios || @driver.device == :tvos
|
252
|
-
end
|
253
|
-
|
254
|
-
return params, selectors
|
255
|
-
end
|
256
|
-
|
257
|
-
|
258
|
-
def resolve_id(id)
|
259
|
-
if id && id.kind_of?(String) && !id.match?(/.*:id\//)
|
260
|
-
# shorthand ids like myId make full ids => my.app.package:id/myId
|
261
|
-
if id[0] == "="
|
262
|
-
return id[1..-1]
|
263
|
-
else
|
264
|
-
return "#{@driver.current_package}:id/#{id}"
|
265
|
-
end
|
266
|
-
else
|
267
|
-
id
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
1
|
+
module TestaAppiumDriver
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
# supported selectors
|
5
|
+
# id: "com.my.package:id/myId"
|
6
|
+
# id: "myId" => will be converted to "com.my.package:id/myId"
|
7
|
+
# id: /my/ will find all elements with ids that contain my
|
8
|
+
# desc: "element description"
|
9
|
+
# desc: /ription/ will find all elements that contains ription
|
10
|
+
# class: "android.widget.Button"
|
11
|
+
# class: /Button/ will find all elements with classes that contain Button
|
12
|
+
# text: "Hello world"
|
13
|
+
# text: /ello/ will find all elements with text that contain ello
|
14
|
+
# package: "com.my.package"
|
15
|
+
# package: /my/ will find all elements with package that contains my
|
16
|
+
# long_clickable: true or false
|
17
|
+
# checkable: true or false
|
18
|
+
# checked: true or false
|
19
|
+
# clickable: true or false
|
20
|
+
# enabled: true or false
|
21
|
+
# focusable: true or false
|
22
|
+
# focused: true or false
|
23
|
+
# index: child index inside of a parent element, index starts from 0
|
24
|
+
# selected: true or false
|
25
|
+
# scrollable: true or false
|
26
|
+
# @param [Hash] hash selectors for finding elements
|
27
|
+
# @param [Boolean] single should the command return first instance or all of matched elements
|
28
|
+
# @return [String] hash selectors converted to uiautomator command
|
29
|
+
def hash_to_uiautomator(hash, single = true)
|
30
|
+
command = "new UiSelector()"
|
31
|
+
|
32
|
+
id = resolve_id(hash[:id])
|
33
|
+
command = "#{ command }.resourceId(\"#{ %(#{ id }) }\")" if id && id.kind_of?(String)
|
34
|
+
command = "#{ command }.resourceIdMatches(\".*#{ %(#{ id.source }) }.*\")" if id && id.kind_of?(Regexp)
|
35
|
+
command = "#{ command }.description(\"#{ %(#{ hash[:desc] }) }\")" if hash[:desc] && hash[:desc].kind_of?(String)
|
36
|
+
command = "#{ command }.descriptionMatches(\".*#{ %(#{ hash[:desc].source }) }.*\")" if hash[:desc] && hash[:desc].kind_of?(Regexp)
|
37
|
+
command = "#{ command }.className(\"#{ %(#{ hash[:class] }) }\")" if hash[:class] && hash[:class].kind_of?(String)
|
38
|
+
command = "#{ command }.classNameMatches(\".*#{ %(#{ hash[:class].source }) }.*\")" if hash[:class] && hash[:class].kind_of?(Regexp)
|
39
|
+
command = "#{ command }.text(\"#{ %(#{ hash[:text] }) }\")" if hash[:text] && hash[:text].kind_of?(String)
|
40
|
+
command = "#{ command }.textMatches(\".*#{ %(#{ hash[:text].source }) }.*\")" if hash[:text] && hash[:text].kind_of?(Regexp)
|
41
|
+
command = "#{ command }.packageName(\"#{ %(#{ hash[:package] }) }\")" if hash[:package] && hash[:package].kind_of?(String)
|
42
|
+
command = "#{ command }.packageNameMatches(\".*#{ %(#{ hash[:package].source }) }.*\")" if hash[:package] && hash[:package].kind_of?(Regexp)
|
43
|
+
|
44
|
+
command = "#{ command }.longClickable(#{ hash[:long_clickable] })" if hash[:long_clickable]
|
45
|
+
command = "#{ command }.checkable(#{ hash[:checkable] })" unless hash[:checkable].nil?
|
46
|
+
command = "#{ command }.checked(#{ hash[:checked] })" unless hash[:checked].nil?
|
47
|
+
command = "#{ command }.clickable(#{ hash[:clickable] })" unless hash[:clickable].nil?
|
48
|
+
command = "#{ command }.enabled(#{ hash[:enabled] })" unless hash[:enabled].nil?
|
49
|
+
command = "#{ command }.focusable(#{ hash[:focusable] })" unless hash[:focusable].nil?
|
50
|
+
command = "#{ command }.focused(#{ hash[:focused] })" unless hash[:focused].nil?
|
51
|
+
command = "#{ command }.index(#{ hash[:index].to_s })" unless hash[:index].nil?
|
52
|
+
command = "#{ command }.selected(#{ hash[:selected] })" unless hash[:selected].nil?
|
53
|
+
command = "#{ command }.scrollable(#{ hash[:scrollable] })" unless hash[:scrollable].nil?
|
54
|
+
|
55
|
+
command += ".instance(0)" if single
|
56
|
+
|
57
|
+
command
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# supported selectors
|
62
|
+
# id: "com.my.package:id/myId"
|
63
|
+
# id: "myId" => will be converted to "com.my.package:id/myId"
|
64
|
+
# id: /my/ will find all elements with ids that contain my
|
65
|
+
# desc: "element description"
|
66
|
+
# desc: /ription/ will find all elements that contains ription
|
67
|
+
# class: "android.widget.Button"
|
68
|
+
# class: /Button/ will find all elements with classes that contain Button
|
69
|
+
# text: "Hello world"
|
70
|
+
# text: /ello/ will find all elements with text that contain ello
|
71
|
+
# package: "com.my.package"
|
72
|
+
# package: /my/ will find all elements with package that contains my
|
73
|
+
# long_clickable: true or false
|
74
|
+
# checkable: true or false
|
75
|
+
# checked: true or false
|
76
|
+
# clickable: true or false
|
77
|
+
# enabled: true or false
|
78
|
+
# focusable: true or false
|
79
|
+
# focused: true or false
|
80
|
+
# index: child index inside of a parent element, index starts from 0
|
81
|
+
# selected: true or false
|
82
|
+
# scrollable: true or false
|
83
|
+
# @param [Hash] hash selectors for finding elements
|
84
|
+
# @param [Boolean] single should the command return first instance or all of matched elements
|
85
|
+
# @return [String] hash selectors converted to xpath command
|
86
|
+
def hash_to_xpath(device, hash, single = true)
|
87
|
+
for_android = device == :android
|
88
|
+
|
89
|
+
|
90
|
+
command = "//"
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
if for_android
|
95
|
+
id = resolve_id(hash[:id])
|
96
|
+
if hash[:class] && hash[:class].kind_of?(String)
|
97
|
+
command = "#{ command }#{hash[:class] }"
|
98
|
+
elsif hash[:class] && hash[:class].kind_of?(Regexp)
|
99
|
+
command = "#{ command}*[contains(@class, \"#{ %(#{hash[:class].source }) }\")]"
|
100
|
+
else
|
101
|
+
command = "#{command}*"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
command = "#{ command }[@resource-id=\"#{ %(#{ id }) }\"]" if id && id.kind_of?(String)
|
106
|
+
command = "#{ command }[contains(@resource-id, \"#{ %(#{ id.source }) }\")]" if id && id.kind_of?(Regexp)
|
107
|
+
command = "#{ command }[@content-desc=\"#{ %(#{hash[:desc] }) }\"]" if hash[:desc] && hash[:desc].kind_of?(String)
|
108
|
+
command = "#{ command }[contains(@content-desc, \"#{ %(#{hash[:desc].source }) }\")]" if hash[:desc] && hash[:desc].kind_of?(Regexp)
|
109
|
+
command = "#{ command }[contains(@class, \"#{ %(#{hash[:class].source }) }\")]" if hash[:class] && hash[:class].kind_of?(Regexp)
|
110
|
+
command = "#{ command }[@text=\"#{ %(#{hash[:text] }) }\"]" if hash[:text] && hash[:text].kind_of?(String)
|
111
|
+
command = "#{ command }[contains(@text, \"#{ %(#{hash[:text].source }) }\")]" if hash[:text] && hash[:text].kind_of?(Regexp)
|
112
|
+
command = "#{ command }[@package=\"#{ %(#{hash[:package] }) }\"]" if hash[:package] && hash[:package].kind_of?(String)
|
113
|
+
command = "#{ command }[contains=(@package, \"#{ %(#{hash[:package].source }) }\")]" if hash[:package] && hash[:package].kind_of?(Regexp)
|
114
|
+
command = "#{ command }[@long-clickable=\"#{ hash[:long_clickable] }\"]" if hash[:long_clickable]
|
115
|
+
command = "#{ command }[@checkable=\"#{ hash[:checkable] }\"]" unless hash[:checkable].nil?
|
116
|
+
command = "#{ command }[@checked=\"#{ hash[:checked] }\"]" unless hash[:checked].nil?
|
117
|
+
command = "#{ command }[@clickable=\"#{ hash[:clickable] }\"]" unless hash[:clickable].nil?
|
118
|
+
command = "#{ command }[@enabled=\"#{ hash[:enabled] }\"]" unless hash[:enabled].nil?
|
119
|
+
command = "#{ command }[@focusable=\"#{ hash[:focusable] }\"]" unless hash[:focusable].nil?
|
120
|
+
command = "#{ command }[@focused=\"#{ hash[:focused] }\"]" unless hash[:focused].nil?
|
121
|
+
command = "#{ command }[@index=\"#{ hash[:index] }\"]" unless hash[:index].nil?
|
122
|
+
command = "#{ command }[@selected=\"#{ hash[:selected] }\"]" unless hash[:selected].nil?
|
123
|
+
|
124
|
+
# it seems like you cannot query by scrollable
|
125
|
+
# command = "#{ command }[@scrollable=\"#{ hash[:scrollable] }\"]" unless hash[:scrollable].nil?
|
126
|
+
else
|
127
|
+
|
128
|
+
hash[:type] = hash[:class] unless hash[:class].nil?
|
129
|
+
if hash[:type] && hash[:type].kind_of?(String)
|
130
|
+
command = "#{ command }#{hash[:type] }"
|
131
|
+
elsif hash[:type] && hash[:type].kind_of?(Regexp)
|
132
|
+
command = "#{ command}*[contains(@type, \"#{ %(#{hash[:type].source }) }\")]"
|
133
|
+
else
|
134
|
+
command = "#{command}*"
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# # ios specific
|
139
|
+
hash[:label] = hash[:text] unless hash[:text].nil?
|
140
|
+
hash[:name] = hash[:id] unless hash[:id].nil?
|
141
|
+
|
142
|
+
command = "#{ command }[@enabled=\"#{ hash[:enabled] }\"]" unless hash[:enabled].nil?
|
143
|
+
command = "#{ command }[@label=\"#{ %(#{hash[:label] }) }\"]" if hash[:label] && hash[:label].kind_of?(String)
|
144
|
+
command = "#{ command }[contains(@label, \"#{ %(#{hash[:label].source }) }\")]" if hash[:label] && hash[:label].kind_of?(Regexp)
|
145
|
+
command = "#{ command }[@name=\"#{ %(#{hash[:name] }) }\"]" if hash[:name] && hash[:name].kind_of?(String)
|
146
|
+
command = "#{ command }[contains(@name, \"#{ %(#{hash[:name].source }) }\")]" if hash[:name] && hash[:name].kind_of?(Regexp)
|
147
|
+
command = "#{ command }[@value=\"#{ %(#{hash[:value] }) }\"]" if hash[:value] && hash[:value].kind_of?(String)
|
148
|
+
command = "#{ command }[contains(@value, \"#{ %(#{hash[:value].source }) }\")]" if hash[:value] && hash[:value].kind_of?(Regexp)
|
149
|
+
command = "#{ command }[@width=\"#{ hash[:width] }\"]" unless hash[:width].nil?
|
150
|
+
command = "#{ command }[@height=\"#{ hash[:height] }\"]" unless hash[:height].nil?
|
151
|
+
command = "#{ command }[@visible=\"#{ hash[:visible] }\"]" unless hash[:visible].nil?
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
command += "[1]" if single
|
156
|
+
|
157
|
+
command
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def hash_to_class_chain(hash, single = true)
|
162
|
+
command = "**/"
|
163
|
+
|
164
|
+
hash[:type] = hash[:class] unless hash[:class].nil?
|
165
|
+
hash[:label] = hash[:text] unless hash[:text].nil?
|
166
|
+
hash[:name] = hash[:id] unless hash[:id].nil?
|
167
|
+
if hash[:type] && hash[:type].kind_of?(String)
|
168
|
+
command = "#{ command }#{hash[:type] }"
|
169
|
+
else
|
170
|
+
command = "#{command}*"
|
171
|
+
end
|
172
|
+
|
173
|
+
command = "#{ command }[`enabled == #{ hash[:enabled] }`]" unless hash[:enabled].nil?
|
174
|
+
command = "#{ command }[`label == \"#{ %(#{hash[:label] }) }\"`]" if hash[:label] && hash[:label].kind_of?(String)
|
175
|
+
command = "#{ command }[`label CONTAINS \"#{ %(#{hash[:label].source }) }\"`]" if hash[:label] && hash[:label].kind_of?(Regexp)
|
176
|
+
command = "#{ command }[`name == \"#{ %(#{hash[:name] }) }\"`]" if hash[:name] && hash[:name].kind_of?(String)
|
177
|
+
command = "#{ command }[`name CONTAINS \"#{ %(#{hash[:name].source }) }\"`]" if hash[:name] && hash[:name].kind_of?(Regexp)
|
178
|
+
command = "#{ command }[`value == \"#{ %(#{hash[:value] }) }\"`]" if hash[:value] && hash[:value].kind_of?(String)
|
179
|
+
command = "#{ command }[`value CONTAINS \"#{ %(#{hash[:value].source }) }\"`]" if hash[:value] && hash[:value].kind_of?(Regexp)
|
180
|
+
command = "#{ command }[`visible == #{ hash[:visible] }`]" unless hash[:visible].nil?
|
181
|
+
|
182
|
+
command += "[1]" if single
|
183
|
+
|
184
|
+
command
|
185
|
+
end
|
186
|
+
|
187
|
+
# check if selectors are for a scrollable element
|
188
|
+
# @param [Boolean] single should the command return first instance or all of matched elements
|
189
|
+
# @param [Hash] selectors for fetching elements
|
190
|
+
# @return [Boolean] true if element has scrollable attribute true or is class one of (RecyclerView, HorizontalScrollView, ScrollView, ListView)
|
191
|
+
def is_scrollable_selector?(selectors, single)
|
192
|
+
return false unless single
|
193
|
+
return true if selectors[:scrollable]
|
194
|
+
if selectors[:class] == "androidx.recyclerview.widget.RecyclerView" ||
|
195
|
+
selectors[:class] == "android.widget.HorizontalScrollView" ||
|
196
|
+
selectors[:class] == "android.widget.ScrollView" ||
|
197
|
+
selectors[:class] == "android.widget.ListView"
|
198
|
+
return true
|
199
|
+
elsif selectors[:type] == "XCUIElementTypeScrollView"
|
200
|
+
return true
|
201
|
+
end
|
202
|
+
false
|
203
|
+
end
|
204
|
+
|
205
|
+
#noinspection RubyUnnecessaryReturnStatement,RubyUnusedLocalVariable
|
206
|
+
# separate selectors from given hash parameters
|
207
|
+
# @param [Hash] params
|
208
|
+
# @return [Array] first element is params, second are selectors
|
209
|
+
def extract_selectors_from_params(params = {})
|
210
|
+
selectors = params.select { |key, value| [
|
211
|
+
:id,
|
212
|
+
:long_clickable,
|
213
|
+
:desc,
|
214
|
+
:class,
|
215
|
+
:text,
|
216
|
+
:package,
|
217
|
+
:checkable,
|
218
|
+
:checked,
|
219
|
+
:clickable,
|
220
|
+
:enabled,
|
221
|
+
:focusable,
|
222
|
+
:focused,
|
223
|
+
:index,
|
224
|
+
:selected,
|
225
|
+
:scrollable,
|
226
|
+
|
227
|
+
# ios specific
|
228
|
+
:type,
|
229
|
+
:label,
|
230
|
+
:x,
|
231
|
+
:y,
|
232
|
+
:width,
|
233
|
+
:height,
|
234
|
+
:visible,
|
235
|
+
:name,
|
236
|
+
:value,
|
237
|
+
|
238
|
+
:image
|
239
|
+
].include?(key) }
|
240
|
+
params = Hash[params.to_a - selectors.to_a]
|
241
|
+
|
242
|
+
# default params
|
243
|
+
params[:single] = true if params[:single].nil?
|
244
|
+
params[:scrollable_locator] = nil if params[:scrollable_locator].nil?
|
245
|
+
if params[:default_find_strategy].nil?
|
246
|
+
params[:default_find_strategy] = DEFAULT_ANDROID_FIND_STRATEGY if @driver.device == :android
|
247
|
+
params[:default_find_strategy] = DEFAULT_IOS_FIND_STRATEGY if @driver.device == :ios || @driver.device == :tvos
|
248
|
+
end
|
249
|
+
if params[:default_scroll_strategy].nil?
|
250
|
+
params[:default_scroll_strategy] = DEFAULT_ANDROID_SCROLL_STRATEGY if @driver.device == :android
|
251
|
+
params[:default_scroll_strategy] = DEFAULT_IOS_SCROLL_STRATEGY if @driver.device == :ios || @driver.device == :tvos
|
252
|
+
end
|
253
|
+
|
254
|
+
return params, selectors
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
def resolve_id(id)
|
259
|
+
if id && id.kind_of?(String) && !id.match?(/.*:id\//)
|
260
|
+
# shorthand ids like myId make full ids => my.app.package:id/myId
|
261
|
+
if id[0] == "="
|
262
|
+
return id[1..-1]
|
263
|
+
else
|
264
|
+
return "#{@driver.current_package}:id/#{id}"
|
265
|
+
end
|
266
|
+
else
|
267
|
+
id
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
271
|
end
|