testa_appium_driver 0.1.6 → 0.1.10
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 +1 -1
- data/.idea/deployment.xml +2 -2
- data/.idea/runConfigurations.xml +10 -0
- data/README.md +18 -5
- data/lib/testa_appium_driver/android/class_selectors.rb +53 -18
- data/lib/testa_appium_driver/android/driver.rb +17 -0
- data/lib/testa_appium_driver/android/locator/attributes.rb +1 -3
- data/lib/testa_appium_driver/android/locator.rb +12 -1
- data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +0 -14
- data/lib/testa_appium_driver/common/constants.rb +2 -0
- data/lib/testa_appium_driver/common/helpers.rb +32 -1
- data/lib/testa_appium_driver/common/locator/scroll_actions.rb +20 -4
- data/lib/testa_appium_driver/common/locator.rb +108 -16
- data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +4 -35
- data/lib/testa_appium_driver/common/scroll_actions.rb +5 -14
- data/lib/testa_appium_driver/driver.rb +78 -32
- data/lib/testa_appium_driver/ios/driver.rb +13 -0
- data/lib/testa_appium_driver/ios/locator.rb +18 -2
- data/lib/testa_appium_driver/ios/type_selectors.rb +36 -15
- data/lib/testa_appium_driver/version.rb +1 -1
- data/testa_appium_driver.iml +4 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81983750f782941f5fb92f5261e63e70a7ec3ebfc539d75cdcb0ae89e7466ee1
|
4
|
+
data.tar.gz: 0ebb35e812a6bbe96b3301e4420fac3ff4cfe0ab4dc6681d4db2b272f7a1fad9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81235f7492aa031cbb25cfda0a74c1e08b14e723b293085e23f145fbf7eff59883fa677720b074306afef4ece650cad6f56e6d5a6f524b5a7eaaa5e160577c47
|
7
|
+
data.tar.gz: 4ad3449934ca4f3cec3b7b886970eb66fc2dc64207ef4cc14d50d21fb33cc0a232bdbe631922ec34e6a88228ab0735c466d58a6527cd11628f4919dc7199ee15
|
data/.gitignore
CHANGED
data/.idea/deployment.xml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<project version="4">
|
3
|
-
<component name="PublishConfigData" autoUpload="On explicit save action" serverName="supertesta.com" autoUploadExternalChanges="true">
|
3
|
+
<component name="PublishConfigData" autoUpload="On explicit save action" serverName="supertesta.com" preserveTimestamps="false" autoUploadExternalChanges="true">
|
4
4
|
<serverData>
|
5
5
|
<paths name="supertesta.com">
|
6
6
|
<serverdata>
|
@@ -12,7 +12,7 @@
|
|
12
12
|
<paths name="testa.fun">
|
13
13
|
<serverdata>
|
14
14
|
<mappings>
|
15
|
-
<mapping
|
15
|
+
<mapping local="$PROJECT_DIR$" web="/" />
|
16
16
|
</mappings>
|
17
17
|
</serverdata>
|
18
18
|
</paths>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="RunConfigurationProducerService">
|
4
|
+
<option name="ignoredProducers">
|
5
|
+
<set>
|
6
|
+
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
7
|
+
</set>
|
8
|
+
</option>
|
9
|
+
</component>
|
10
|
+
</project>
|
data/README.md
CHANGED
@@ -202,12 +202,18 @@ siblings selector can only be used with xpath strategy.
|
|
202
202
|
- search_views
|
203
203
|
- spinner
|
204
204
|
- spinners
|
205
|
+
- switch
|
206
|
+
- switches
|
205
207
|
- toast
|
206
208
|
- toasts
|
207
209
|
- toolbar
|
208
210
|
- toolbars
|
209
211
|
- text_view
|
210
212
|
- text_views
|
213
|
+
- web_view
|
214
|
+
- web_views
|
215
|
+
- card_view
|
216
|
+
- card_views
|
211
217
|
|
212
218
|
Adjacent selectors
|
213
219
|
- from_parent
|
@@ -323,11 +329,15 @@ Type Selector arguments
|
|
323
329
|
|
324
330
|
# Scroll actions
|
325
331
|
- each
|
326
|
-
-
|
327
|
-
-
|
328
|
-
-
|
329
|
-
-
|
330
|
-
-
|
332
|
+
- each_down
|
333
|
+
- each_up
|
334
|
+
- each_left
|
335
|
+
- each_right
|
336
|
+
- align! (if does not exist, will scroll to find)
|
337
|
+
- align_top! (if does not exist, will scroll to find)
|
338
|
+
- align_bottom! (if does not exist, will scroll to find)
|
339
|
+
- align_left! (if does not exist, will scroll to find)
|
340
|
+
- align_right! (if does not exist, will scroll to find)
|
331
341
|
- align
|
332
342
|
- align_top
|
333
343
|
- align_bottom
|
@@ -355,6 +365,9 @@ Type Selector arguments
|
|
355
365
|
- as_scrollable
|
356
366
|
- wait_until_exists
|
357
367
|
- wait_while_exists
|
368
|
+
- wait_until
|
369
|
+
- wait_while
|
370
|
+
- when_exists
|
358
371
|
- exists?
|
359
372
|
- long_tap
|
360
373
|
|
@@ -24,6 +24,9 @@ module TestaAppiumDriver
|
|
24
24
|
# @param selectors [Hash]
|
25
25
|
# @return [TestaAppiumDriver::Locator] first element
|
26
26
|
def element(selectors = {})
|
27
|
+
unless selectors[:image].nil?
|
28
|
+
selectors[:strategy] = FIND_STRATEGY_IMAGE
|
29
|
+
end
|
27
30
|
add_selector(selectors)
|
28
31
|
end
|
29
32
|
|
@@ -31,24 +34,9 @@ module TestaAppiumDriver
|
|
31
34
|
# @param params [Hash]
|
32
35
|
# @return [TestaAppiumDriver::Locator] all elements that match given selectors
|
33
36
|
def elements(params = {})
|
34
|
-
params[:
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# first element that has scrollable: true
|
40
|
-
# @param selectors [Hash]
|
41
|
-
# @return [TestaAppiumDriver::Locator] first scrollable element
|
42
|
-
def scrollable(selectors = {})
|
43
|
-
selectors[:scrollable] = true
|
44
|
-
add_selector(selectors)
|
45
|
-
end
|
46
|
-
|
47
|
-
# all elements that have scrollable: true
|
48
|
-
# @param params [Hash]
|
49
|
-
# @return [TestaAppiumDriver::Locator] first scrollable element
|
50
|
-
def scrollables(params = {})
|
51
|
-
params[:scrollable] = true
|
37
|
+
unless params[:image].nil?
|
38
|
+
params[:strategy] = FIND_STRATEGY_IMAGE
|
39
|
+
end
|
52
40
|
params[:single] = false
|
53
41
|
add_selector(params)
|
54
42
|
end
|
@@ -399,5 +387,52 @@ module TestaAppiumDriver
|
|
399
387
|
add_selector(params)
|
400
388
|
end
|
401
389
|
|
390
|
+
|
391
|
+
# first androidx.cardview.widget.CardView element that match given selectors
|
392
|
+
# @return [TestaAppiumDriver::Locator]
|
393
|
+
def card_view(params = {})
|
394
|
+
params[:class] = "androidx.cardview.widget.CardView"
|
395
|
+
add_selector(params)
|
396
|
+
end
|
397
|
+
|
398
|
+
# all androidx.cardview.widget.CardView elements that match given selectors
|
399
|
+
# @return [TestaAppiumDriver::Locator]
|
400
|
+
def card_views(params = {})
|
401
|
+
params[:class] = "androidx.cardview.widget.CardView"
|
402
|
+
params[:single] = false
|
403
|
+
add_selector(params)
|
404
|
+
end
|
405
|
+
|
406
|
+
# first android.widget.Switch element that match given selectors
|
407
|
+
# @return [TestaAppiumDriver::Locator]
|
408
|
+
def switch(params = {})
|
409
|
+
params[:class] = "android.widget.Switch"
|
410
|
+
add_selector(params)
|
411
|
+
end
|
412
|
+
|
413
|
+
# all android.widget.Switch elements that match given selectors
|
414
|
+
# @return [TestaAppiumDriver::Locator]
|
415
|
+
def switches(params = {})
|
416
|
+
params[:class] = "android.widget.Switch"
|
417
|
+
params[:single] = false
|
418
|
+
add_selector(params)
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
# first android.webkit.WebView element that match given selectors
|
423
|
+
# @return [TestaAppiumDriver::Locator]
|
424
|
+
def web_view(params = {})
|
425
|
+
params[:class] = "android.webkit.WebView"
|
426
|
+
add_selector(params)
|
427
|
+
end
|
428
|
+
|
429
|
+
# all android.webkit.WebView elements that match given selectors
|
430
|
+
# @return [TestaAppiumDriver::Locator]
|
431
|
+
def web_views(params = {})
|
432
|
+
params[:class] = "android.webkit.WebView"
|
433
|
+
params[:single] = false
|
434
|
+
add_selector(params)
|
435
|
+
end
|
436
|
+
|
402
437
|
end
|
403
438
|
end
|
@@ -24,6 +24,23 @@ module TestaAppiumDriver
|
|
24
24
|
end
|
25
25
|
|
26
26
|
|
27
|
+
def scrollable
|
28
|
+
locator = Locator.new(self, self, {single: true})
|
29
|
+
locator.xpath_selector = "//androidx.recyclerview.widget.RecyclerView|//android.widget.ScrollView|//android.widget.ListView|//android.widget.HorizontalScrollView"
|
30
|
+
locator.ui_selector = "new UiSelector().scrollable(true).instance(0)"
|
31
|
+
locator
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def scrollables
|
36
|
+
locator = Locator.new(self, self, {single: false})
|
37
|
+
locator.xpath_selector = "//androidx.recyclerview.widget.RecyclerView|//android.widget.ScrollView|//android.widget.ListView|//android.widget.HorizontalScrollView"
|
38
|
+
locator.ui_selector = "new UiSelector().scrollable(true)"
|
39
|
+
locator
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
27
44
|
private
|
28
45
|
def handle_testa_opts
|
29
46
|
if @testa_opts[:default_find_strategy].nil?
|
@@ -7,7 +7,6 @@ module TestaAppiumDriver
|
|
7
7
|
|
8
8
|
@driver = get_driver if self.instance_of?(Selenium::WebDriver::Element)
|
9
9
|
|
10
|
-
@driver.disable_wait_for_idle
|
11
10
|
if elements.kind_of?(Selenium::WebDriver::Element)
|
12
11
|
r = elements.send(:attribute, name.to_s)
|
13
12
|
r = TestaAppiumDriver::Bounds.from_android(r, @driver) if name.to_s == "bounds"
|
@@ -15,7 +14,6 @@ module TestaAppiumDriver
|
|
15
14
|
r = elements.map { |e| e.send(:attribute, name.to_s) }
|
16
15
|
r.map! { |b| TestaAppiumDriver::Bounds.from_android(b, @driver) } if name.to_s == "bounds"
|
17
16
|
end
|
18
|
-
@driver.enable_wait_for_idle
|
19
17
|
r
|
20
18
|
end
|
21
19
|
|
@@ -110,7 +108,7 @@ module TestaAppiumDriver
|
|
110
108
|
children = self.dup.parent.children.execute
|
111
109
|
index = children.index(this)
|
112
110
|
raise "Index not found" if index.nil?
|
113
|
-
index
|
111
|
+
index.to_i
|
114
112
|
end
|
115
113
|
end
|
116
114
|
end
|
@@ -48,6 +48,10 @@ module TestaAppiumDriver
|
|
48
48
|
if @strategy.nil? || @strategy == FIND_STRATEGY_XPATH
|
49
49
|
ss.push({"#{FIND_STRATEGY_XPATH}": @xpath_selector})
|
50
50
|
end
|
51
|
+
|
52
|
+
if @strategy == FIND_STRATEGY_IMAGE
|
53
|
+
ss.push({"#{FIND_STRATEGY_IMAGE}": @image_selector})
|
54
|
+
end
|
51
55
|
ss
|
52
56
|
end
|
53
57
|
|
@@ -93,6 +97,8 @@ module TestaAppiumDriver
|
|
93
97
|
add_xpath_child_selectors(locator, selectors, single)
|
94
98
|
elsif @strategy == FIND_STRATEGY_UIAUTOMATOR
|
95
99
|
locator = add_uiautomator_child_selector(locator, selectors, single)
|
100
|
+
elsif @strategy == FIND_STRATEGY_IMAGE
|
101
|
+
locator = add_image_child_selector(locator, selectors, single)
|
96
102
|
else
|
97
103
|
# both paths are valid
|
98
104
|
add_xpath_child_selectors(locator, selectors, single)
|
@@ -100,7 +106,7 @@ module TestaAppiumDriver
|
|
100
106
|
end
|
101
107
|
|
102
108
|
if is_scrollable_selector?(selectors, single)
|
103
|
-
locator.scrollable_locator =
|
109
|
+
locator.scrollable_locator = locator
|
104
110
|
if selectors[:class] == "android.widget.HorizontalScrollView"
|
105
111
|
locator.scrollable_locator.scroll_orientation = :horizontal
|
106
112
|
else
|
@@ -127,5 +133,10 @@ module TestaAppiumDriver
|
|
127
133
|
locator
|
128
134
|
end
|
129
135
|
end
|
136
|
+
|
137
|
+
def add_image_child_selector(locator, selectors, single)
|
138
|
+
params = selectors.merge({single: single, scrollable_locator: locator.scrollable_locator})
|
139
|
+
Locator.new(@driver, self, params)
|
140
|
+
end
|
130
141
|
end
|
131
142
|
end
|
@@ -4,8 +4,6 @@ module TestaAppiumDriver
|
|
4
4
|
private
|
5
5
|
|
6
6
|
def uiautomator_scroll_to_start_or_end(type)
|
7
|
-
@driver.disable_wait_for_idle
|
8
|
-
@driver.disable_implicit_wait
|
9
7
|
|
10
8
|
scrollable_selector = @scrollable.ui_selector(false)
|
11
9
|
orientation = @scrollable.scroll_orientation == :vertical ? ".setAsVerticalList()" : ".setAsHorizontalList()"
|
@@ -19,18 +17,12 @@ module TestaAppiumDriver
|
|
19
17
|
end
|
20
18
|
|
21
19
|
|
22
|
-
@driver.enable_implicit_wait
|
23
|
-
@driver.enable_wait_for_idle
|
24
20
|
end
|
25
21
|
|
26
22
|
|
27
23
|
def uiautomator_scroll_to
|
28
24
|
raise "UiAutomator scroll cannot work with specified direction" unless @direction.nil?
|
29
25
|
|
30
|
-
|
31
|
-
@driver.disable_wait_for_idle
|
32
|
-
@driver.disable_implicit_wait
|
33
|
-
|
34
26
|
scrollable_selector = @scrollable.ui_selector(false)
|
35
27
|
element_selector = @locator.ui_selector(false)
|
36
28
|
orientation_command = @scrollable.scroll_orientation == :vertical ? ".setAsVerticalList()" : ".setAsHorizontalList()"
|
@@ -41,15 +33,11 @@ module TestaAppiumDriver
|
|
41
33
|
rescue
|
42
34
|
# Ignored
|
43
35
|
ensure
|
44
|
-
@driver.enable_implicit_wait
|
45
|
-
@driver.enable_wait_for_idle
|
46
36
|
end
|
47
37
|
end
|
48
38
|
|
49
39
|
|
50
40
|
def uiautomator_page_or_fling(type, direction)
|
51
|
-
@driver.disable_wait_for_idle
|
52
|
-
@driver.disable_implicit_wait
|
53
41
|
|
54
42
|
scrollable_selector = @scrollable.ui_selector(false)
|
55
43
|
orientation = direction == :up || direction == :down ? ".setAsVerticalList()" : ".setAsHorizontalList()"
|
@@ -67,8 +55,6 @@ module TestaAppiumDriver
|
|
67
55
|
rescue
|
68
56
|
# Ignored
|
69
57
|
end
|
70
|
-
@driver.enable_implicit_wait
|
71
|
-
@driver.enable_wait_for_idle
|
72
58
|
end
|
73
59
|
|
74
60
|
|
@@ -120,7 +120,9 @@ module TestaAppiumDriver
|
|
120
120
|
command = "#{ command }[@focused=\"#{ hash[:focused] }\"]" unless hash[:focused].nil?
|
121
121
|
command = "#{ command }[@index=\"#{ hash[:index] }\"]" unless hash[:index].nil?
|
122
122
|
command = "#{ command }[@selected=\"#{ hash[:selected] }\"]" unless hash[:selected].nil?
|
123
|
-
|
123
|
+
|
124
|
+
# it seems like you cannot query by scrollable
|
125
|
+
# command = "#{ command }[@scrollable=\"#{ hash[:scrollable] }\"]" unless hash[:scrollable].nil?
|
124
126
|
else
|
125
127
|
|
126
128
|
hash[:type] = hash[:class] unless hash[:class].nil?
|
@@ -155,6 +157,33 @@ module TestaAppiumDriver
|
|
155
157
|
command
|
156
158
|
end
|
157
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
|
+
|
158
187
|
# check if selectors are for a scrollable element
|
159
188
|
# @param [Boolean] single should the command return first instance or all of matched elements
|
160
189
|
# @param [Hash] selectors for fetching elements
|
@@ -205,6 +234,8 @@ module TestaAppiumDriver
|
|
205
234
|
:visible,
|
206
235
|
:name,
|
207
236
|
:value,
|
237
|
+
|
238
|
+
:image
|
208
239
|
].include?(key) }
|
209
240
|
params = Hash[params.to_a - selectors.to_a]
|
210
241
|
|
@@ -250,6 +250,22 @@ module TestaAppiumDriver
|
|
250
250
|
_fling(:right, _process_deadzone(top, bottom, right, left))
|
251
251
|
end
|
252
252
|
|
253
|
+
def drag_up_by(amount)
|
254
|
+
drag_by(amount, direction: :top)
|
255
|
+
end
|
256
|
+
|
257
|
+
def drag_down_by(amount)
|
258
|
+
drag_by(amount, direction: :bottom)
|
259
|
+
end
|
260
|
+
|
261
|
+
def drag_left_by(amount)
|
262
|
+
drag_by(amount, direction: :left)
|
263
|
+
end
|
264
|
+
|
265
|
+
def drag_right_by(amount)
|
266
|
+
drag_by(amount, direction: :right)
|
267
|
+
end
|
268
|
+
|
253
269
|
|
254
270
|
# @param [TestaAppiumDriver::Locator, Hash, Selenium::WebDriver::Element, String] to
|
255
271
|
#noinspection RubyYardParamTypeMatch,RubyScope
|
@@ -273,7 +289,7 @@ module TestaAppiumDriver
|
|
273
289
|
x = to[:x]
|
274
290
|
y = to[:y]
|
275
291
|
end
|
276
|
-
_drag_to(x, y)
|
292
|
+
_drag_to(bounds.center.x, bounds.center.y, x, y)
|
277
293
|
end
|
278
294
|
|
279
295
|
def drag_by(amount, direction: :top)
|
@@ -292,7 +308,7 @@ module TestaAppiumDriver
|
|
292
308
|
else
|
293
309
|
raise "Unknown direction #{direction}"
|
294
310
|
end
|
295
|
-
_drag_to(x, y)
|
311
|
+
_drag_to(b.center.x, b.center.y, x, y)
|
296
312
|
end
|
297
313
|
|
298
314
|
|
@@ -310,11 +326,11 @@ module TestaAppiumDriver
|
|
310
326
|
deadzone
|
311
327
|
end
|
312
328
|
|
313
|
-
def _drag_to(
|
329
|
+
def _drag_to(x0, y0, x1, y1)
|
314
330
|
sa = ScrollActions.new(@scrollable_locator,
|
315
331
|
locator: self,
|
316
332
|
default_scroll_strategy: @default_scroll_strategy)
|
317
|
-
sa.drag_to(
|
333
|
+
sa.drag_to(x0, y0, x1, y1)
|
318
334
|
self
|
319
335
|
end
|
320
336
|
|
@@ -17,6 +17,8 @@ module TestaAppiumDriver
|
|
17
17
|
attr_accessor :last_selector_adjacent
|
18
18
|
attr_accessor :can_use_id_strategy
|
19
19
|
|
20
|
+
attr_accessor :image_selector
|
21
|
+
|
20
22
|
attr_accessor :from_element
|
21
23
|
attr_accessor :scroll_orientation
|
22
24
|
attr_accessor :scroll_deadzone
|
@@ -41,12 +43,21 @@ module TestaAppiumDriver
|
|
41
43
|
# @type [TestaAppiumDriver::Driver]
|
42
44
|
@driver = driver
|
43
45
|
@index_for_multiple = nil
|
46
|
+
@image_selector = nil
|
44
47
|
|
45
48
|
params, selectors = extract_selectors_from_params(params)
|
46
49
|
single = params[:single]
|
47
50
|
|
48
51
|
@single = single
|
49
52
|
|
53
|
+
if selectors[:image].nil?
|
54
|
+
if from_element.instance_of?(TestaAppiumDriver::Locator) && !from_element.image_selector.nil?
|
55
|
+
raise "Cannot chain non-image selectors to image selectors"
|
56
|
+
end
|
57
|
+
else
|
58
|
+
handle_image_selector(selectors, params)
|
59
|
+
end
|
60
|
+
|
50
61
|
selectors[:id] = selectors[:name] unless selectors[:name].nil?
|
51
62
|
if from_element.instance_of?(Selenium::WebDriver::Element)
|
52
63
|
@xpath_selector = "//*" # to select current element
|
@@ -80,6 +91,8 @@ module TestaAppiumDriver
|
|
80
91
|
end
|
81
92
|
|
82
93
|
|
94
|
+
|
95
|
+
|
83
96
|
def is_only_id_selector?(selectors)
|
84
97
|
# since, name and id is the same thing for iOS,
|
85
98
|
if @driver.device == :android
|
@@ -95,7 +108,6 @@ module TestaAppiumDriver
|
|
95
108
|
def method_missing(method, *args, &block)
|
96
109
|
r = execute.send(method, *args, &block)
|
97
110
|
@driver.invalidate_cache
|
98
|
-
r = r[@index_for_multiple] if !@index_for_multiple.nil? && !@single
|
99
111
|
r
|
100
112
|
end
|
101
113
|
|
@@ -103,14 +115,16 @@ module TestaAppiumDriver
|
|
103
115
|
# @param [Boolean] skip_cache if true it will skip cache check and store
|
104
116
|
# @param [Selenium::WebDriver::Element] force_cache_element, for internal use where we have already the element, and want to execute custom locator methods on it
|
105
117
|
# @return [Selenium::WebDriver::Element, Array]
|
106
|
-
def execute(skip_cache: false, force_cache_element: nil)
|
118
|
+
def execute(skip_cache: false, force_cache_element: nil, ignore_implicit_wait: false)
|
107
119
|
return force_cache_element unless force_cache_element.nil?
|
108
120
|
# if we are looking for current element, then return from_element
|
109
121
|
# for example when we have driver.element.elements[1].click
|
110
122
|
# elements[2] will be resolved with xpath because we are looking for multiple elements from element
|
111
123
|
# and since we are looking for instance 2, [](instance) method will return new "empty locator"
|
112
124
|
# we are executing click on that "empty locator" so we have to return the instance 2 of elements for the click
|
113
|
-
if @xpath_selector == "
|
125
|
+
if @xpath_selector == "//*[1]" && !@from_element.nil? && @image_selector.nil?
|
126
|
+
return @from_element if @from_element.instance_of?(Selenium::WebDriver::Element)
|
127
|
+
return @from_element.execute(skip_cache: skip_cache, force_cache_element: force_cache_element, ignore_implicit_wait: ignore_implicit_wait) if @from_element.instance_of?(TestaAppiumDriver::Locator)
|
114
128
|
return @from_element
|
115
129
|
end
|
116
130
|
|
@@ -119,11 +133,30 @@ module TestaAppiumDriver
|
|
119
133
|
|
120
134
|
|
121
135
|
|
122
|
-
r = @driver.execute(@from_element, @single, strategies_and_selectors, skip_cache)
|
136
|
+
r = @driver.execute(@from_element, @single, strategies_and_selectors, skip_cache: skip_cache, ignore_implicit_wait: ignore_implicit_wait)
|
123
137
|
r = r[@index_for_multiple] if !@index_for_multiple.nil? && !@single
|
124
138
|
r
|
125
139
|
end
|
126
140
|
|
141
|
+
def when_exists(timeout = nil, &block)
|
142
|
+
timeout = @driver.get_timeouts["implicit"] / 1000 if timeout.nil?
|
143
|
+
found = false
|
144
|
+
begin
|
145
|
+
wait_until_exists(timeout)
|
146
|
+
found = true
|
147
|
+
rescue
|
148
|
+
#ignored
|
149
|
+
end
|
150
|
+
if found
|
151
|
+
if block_given? # block is given
|
152
|
+
block.call(self) # use call to execute the block
|
153
|
+
else # the value of block_argument becomes nil if you didn't give a block
|
154
|
+
# block was not given
|
155
|
+
end
|
156
|
+
end
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
127
160
|
|
128
161
|
# @param [Integer] timeout in seconds
|
129
162
|
# @return [TestaAppiumDriver::Locator]
|
@@ -157,16 +190,12 @@ module TestaAppiumDriver
|
|
157
190
|
# all timeouts are disabled before check, and enabled after check
|
158
191
|
# @return [boolean] true if it exists in the page regardless if visible or not
|
159
192
|
def exists?
|
160
|
-
@driver.disable_wait_for_idle
|
161
|
-
@driver.disable_implicit_wait
|
162
193
|
found = true
|
163
194
|
begin
|
164
|
-
execute(skip_cache: true)
|
195
|
+
execute(skip_cache: true, ignore_implicit_wait: true)
|
165
196
|
rescue StandardError
|
166
197
|
found = false
|
167
198
|
end
|
168
|
-
@driver.enable_implicit_wait
|
169
|
-
@driver.enable_wait_for_idle
|
170
199
|
found
|
171
200
|
end
|
172
201
|
|
@@ -192,15 +221,23 @@ module TestaAppiumDriver
|
|
192
221
|
|
193
222
|
def [](instance)
|
194
223
|
raise "Cannot add index selector to non-Array" if @single
|
195
|
-
if ((@strategy.nil? && !@last_selector_adjacent) || @strategy == FIND_STRATEGY_UIAUTOMATOR) && instance >= 0
|
224
|
+
if ((@strategy.nil? && !@last_selector_adjacent && @driver.device == :android) || @strategy == FIND_STRATEGY_UIAUTOMATOR) && instance >= 0
|
196
225
|
locator = self.dup
|
197
226
|
locator.strategy = FIND_STRATEGY_UIAUTOMATOR
|
198
227
|
locator.ui_selector = "#{@ui_selector}.instance(#{instance})"
|
199
228
|
locator.single = true
|
200
229
|
locator.can_use_id_strategy = false
|
201
230
|
locator
|
231
|
+
elsif (@driver.device == :ios && !@last_selector_adjacent && @strategy.nil?) || @strategy == FIND_STRATEGY_CLASS_CHAIN
|
232
|
+
locator = self.dup
|
233
|
+
locator.strategy = FIND_STRATEGY_CLASS_CHAIN
|
234
|
+
locator.class_chain_selector += "[#{instance + 1}]"
|
235
|
+
locator.single = true
|
236
|
+
locator.can_use_id_strategy = false
|
237
|
+
locator
|
202
238
|
else
|
203
|
-
from_element = self.
|
239
|
+
from_element = self.dup
|
240
|
+
from_element.index_for_multiple = instance
|
204
241
|
params = {}.merge({single: true, scrollable_locator: @scrollable_locator})
|
205
242
|
#params[:strategy] = FIND_STRATEGY_XPATH
|
206
243
|
#params[:strategy_reason] = "retrieved instance of a array"
|
@@ -276,7 +313,15 @@ module TestaAppiumDriver
|
|
276
313
|
end
|
277
314
|
|
278
315
|
def click
|
279
|
-
|
316
|
+
if @driver.device == :android
|
317
|
+
perform_driver_method(:click)
|
318
|
+
else
|
319
|
+
# on ios, if element is not visible, first click will scroll to it
|
320
|
+
# then on second click actually perform the click
|
321
|
+
visible = visible?
|
322
|
+
perform_driver_method(:click)
|
323
|
+
perform_driver_method(:click) unless visible rescue nil
|
324
|
+
end
|
280
325
|
end
|
281
326
|
|
282
327
|
def send_key(*args)
|
@@ -306,15 +351,20 @@ module TestaAppiumDriver
|
|
306
351
|
# @return [TestaAppiumDriver::Locator]
|
307
352
|
def children
|
308
353
|
raise "Cannot add children selector to array" unless @single
|
309
|
-
raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "children") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
|
354
|
+
raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "children") if @strategy != FIND_STRATEGY_XPATH && @strategy != FIND_STRATEGY_CLASS_CHAIN && !@strategy.nil?
|
310
355
|
|
311
356
|
locator = self.dup
|
312
|
-
locator.strategy = FIND_STRATEGY_XPATH
|
313
357
|
locator.strategy_reason = "children"
|
314
358
|
locator.xpath_selector += "/*"
|
315
359
|
locator.single = false
|
316
360
|
locator.last_selector_adjacent = true
|
317
361
|
locator.can_use_id_strategy = false
|
362
|
+
|
363
|
+
if @driver.device == :android
|
364
|
+
locator.strategy = FIND_STRATEGY_XPATH
|
365
|
+
else
|
366
|
+
locator.class_chain_selector += "/*"
|
367
|
+
end
|
318
368
|
locator
|
319
369
|
end
|
320
370
|
|
@@ -323,14 +373,20 @@ module TestaAppiumDriver
|
|
323
373
|
# @return [TestaAppiumDriver::Locator]
|
324
374
|
def child
|
325
375
|
raise "Cannot add children selector to array" unless @single
|
326
|
-
raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "child") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
|
376
|
+
raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "child") if @strategy != FIND_STRATEGY_XPATH && @strategy != FIND_STRATEGY_CLASS_CHAIN && !@strategy.nil?
|
327
377
|
|
328
378
|
locator = self.dup
|
329
|
-
|
379
|
+
|
330
380
|
locator.strategy_reason = "child"
|
331
381
|
locator.xpath_selector += "/*[1]"
|
332
382
|
locator.single = true
|
333
383
|
locator.can_use_id_strategy = false
|
384
|
+
|
385
|
+
if @driver.device == :android
|
386
|
+
locator.strategy = FIND_STRATEGY_XPATH
|
387
|
+
else
|
388
|
+
locator.class_chain_selector += "/*[1]"
|
389
|
+
end
|
334
390
|
locator
|
335
391
|
end
|
336
392
|
|
@@ -482,6 +538,42 @@ module TestaAppiumDriver
|
|
482
538
|
locator.single = false unless single # switching from single result to multiple
|
483
539
|
locator.xpath_selector += hash_to_xpath(@driver.device, selectors, single)
|
484
540
|
end
|
541
|
+
|
542
|
+
|
543
|
+
def handle_image_selector(selectors, params)
|
544
|
+
image_match_threshold = 0.4
|
545
|
+
image_match_threshold = params[:imageMatchThreshold] unless params[:imageMatchThreshold].nil?
|
546
|
+
image_match_threshold = params[:threshold] unless params[:threshold].nil?
|
547
|
+
fix_image_find_screenshot_dims = true
|
548
|
+
fix_image_find_screenshot_dims = params[:fixImageFindScreenshotDims] unless params[:fixImageFindScreenshotDims].nil?
|
549
|
+
fix_image_template_size = false
|
550
|
+
fix_image_template_size = params[:fixImageTemplateSize] unless params[:fixImageTemplateSize].nil?
|
551
|
+
fix_image_template_scale = false
|
552
|
+
fix_image_template_scale = params[:fixImageTemplateScale] unless params[:fixImageTemplateScale].nil?
|
553
|
+
default_image_template_scale = 1.0
|
554
|
+
default_image_template_scale = params[:defaultImageTemplateScale] unless params[:defaultImageTemplateScale].nil?
|
555
|
+
check_for_image_element_staleness = true
|
556
|
+
check_for_image_element_staleness = params[:checkForImageElementStaleness] unless params[:checkForImageElementStaleness].nil?
|
557
|
+
auto_update_image_element_position = false
|
558
|
+
auto_update_image_element_position = params[:autoUpdateImageElementPosition] unless params[:autoUpdateImageElementPosition].nil?
|
559
|
+
image_element_tap_strategy = "w3cActions"
|
560
|
+
image_element_tap_strategy = params[:imageElementTapStrategy] unless params[:imageElementTapStrategy].nil?
|
561
|
+
get_matched_image_result = false
|
562
|
+
get_matched_image_result = params[:getMatchedImageResult] unless params[:getMatchedImageResult].nil?
|
563
|
+
|
564
|
+
@image_selector = {
|
565
|
+
image: selectors[:image],
|
566
|
+
imageMatchThreshold: image_match_threshold,
|
567
|
+
fixImageFindScreenshotDims: fix_image_find_screenshot_dims,
|
568
|
+
fixImageTemplateSize: fix_image_template_size,
|
569
|
+
fixImageTemplateScale: fix_image_template_scale,
|
570
|
+
defaultImageTemplateScale: default_image_template_scale,
|
571
|
+
checkForImageElementStaleness: check_for_image_element_staleness,
|
572
|
+
autoUpdateImageElementPosition: auto_update_image_element_position,
|
573
|
+
imageElementTapStrategy: image_element_tap_strategy,
|
574
|
+
getMatchedImageResult: get_matched_image_result,
|
575
|
+
}
|
576
|
+
end
|
485
577
|
end
|
486
578
|
|
487
579
|
end
|
@@ -6,8 +6,6 @@ module TestaAppiumDriver
|
|
6
6
|
def w3c_each(direction, &block)
|
7
7
|
elements = []
|
8
8
|
begin
|
9
|
-
@driver.disable_wait_for_idle
|
10
|
-
@driver.disable_implicit_wait
|
11
9
|
default_deadzone!
|
12
10
|
|
13
11
|
iterations = 0
|
@@ -25,6 +23,7 @@ module TestaAppiumDriver
|
|
25
23
|
until is_end_of_scroll?
|
26
24
|
matches = @locator.execute(skip_cache: true)
|
27
25
|
matches.each_with_index do |m|
|
26
|
+
next if elements.include?(m)
|
28
27
|
elements << m
|
29
28
|
if block_given? # block is given
|
30
29
|
block.call(m) # use call to execute the block
|
@@ -38,15 +37,12 @@ module TestaAppiumDriver
|
|
38
37
|
end
|
39
38
|
rescue => e
|
40
39
|
raise e
|
41
|
-
|
42
|
-
@driver.enable_implicit_wait
|
43
|
-
@driver.enable_wait_for_idle
|
40
|
+
|
44
41
|
end
|
45
42
|
elements
|
46
43
|
end
|
47
44
|
|
48
45
|
def w3c_align(with, scroll_to_find)
|
49
|
-
@driver.disable_wait_for_idle
|
50
46
|
default_deadzone!
|
51
47
|
|
52
48
|
|
@@ -54,20 +50,6 @@ module TestaAppiumDriver
|
|
54
50
|
@locator.scroll_to if scroll_to_find
|
55
51
|
|
56
52
|
element = @locator.execute
|
57
|
-
@driver.disable_implicit_wait
|
58
|
-
|
59
|
-
case with
|
60
|
-
when :top
|
61
|
-
page_down if is_aligned?(with, element)
|
62
|
-
when :bottom
|
63
|
-
page_up if is_aligned?(with, element)
|
64
|
-
when :right
|
65
|
-
page_right if is_aligned?(with, element)
|
66
|
-
when :left
|
67
|
-
page_left if is_aligned?(with, element)
|
68
|
-
else
|
69
|
-
raise "Unsupported align with option: #{with}"
|
70
|
-
end
|
71
53
|
|
72
54
|
timeout = 0
|
73
55
|
until is_aligned?(with, element) || timeout == 3
|
@@ -75,8 +57,6 @@ module TestaAppiumDriver
|
|
75
57
|
timeout += 1
|
76
58
|
end
|
77
59
|
|
78
|
-
@driver.enable_implicit_wait
|
79
|
-
@driver.enable_wait_for_idle
|
80
60
|
end
|
81
61
|
|
82
62
|
|
@@ -150,8 +130,6 @@ module TestaAppiumDriver
|
|
150
130
|
end
|
151
131
|
|
152
132
|
def w3c_scroll_to_start_or_end(type)
|
153
|
-
@driver.disable_wait_for_idle
|
154
|
-
@driver.disable_implicit_wait
|
155
133
|
default_deadzone!
|
156
134
|
|
157
135
|
@previous_elements = nil
|
@@ -179,15 +157,10 @@ module TestaAppiumDriver
|
|
179
157
|
|
180
158
|
# reset the flag for end of scroll elements
|
181
159
|
@previous_elements = nil
|
182
|
-
|
183
|
-
@driver.enable_implicit_wait
|
184
|
-
@driver.enable_wait_for_idle
|
185
160
|
end
|
186
161
|
|
187
162
|
|
188
163
|
def w3c_page_or_fling(type, direction)
|
189
|
-
@driver.disable_wait_for_idle
|
190
|
-
@driver.disable_implicit_wait
|
191
164
|
default_deadzone!
|
192
165
|
|
193
166
|
if direction == :down || direction == :up
|
@@ -216,8 +189,6 @@ module TestaAppiumDriver
|
|
216
189
|
|
217
190
|
w3c_action(x0, y0, x1, y1, type)
|
218
191
|
|
219
|
-
@driver.enable_implicit_wait
|
220
|
-
@driver.enable_wait_for_idle
|
221
192
|
end
|
222
193
|
|
223
194
|
|
@@ -258,10 +229,8 @@ module TestaAppiumDriver
|
|
258
229
|
|
259
230
|
|
260
231
|
|
261
|
-
def
|
262
|
-
x0
|
263
|
-
y0 = @bounds.center.y
|
264
|
-
w3c_action(x0, y0, x, y, SCROLL_ACTION_TYPE_DRAG)
|
232
|
+
def w3c_drag_to(x0, y0, x1, y1)
|
233
|
+
w3c_action(x0, y0, x1, y1, SCROLL_ACTION_TYPE_DRAG)
|
265
234
|
end
|
266
235
|
|
267
236
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require_relative 'scroll_actions/json_wire_scroll_actions'
|
2
2
|
require_relative 'scroll_actions/w3c_scroll_actions'
|
3
3
|
|
4
|
-
|
5
4
|
module TestaAppiumDriver
|
6
5
|
|
7
6
|
# Class for handling scroll actions
|
@@ -21,7 +20,6 @@ module TestaAppiumDriver
|
|
21
20
|
@default_scroll_strategy = params[:default_scroll_strategy]
|
22
21
|
@driver = @locator.driver
|
23
22
|
|
24
|
-
|
25
23
|
if @scrollable.nil?
|
26
24
|
# if we dont have a scrollable element or if we do have it, but it is not compatible with uiautomator
|
27
25
|
# then find first scrollable in document
|
@@ -30,16 +28,14 @@ module TestaAppiumDriver
|
|
30
28
|
|
31
29
|
@strategy = nil
|
32
30
|
if @scrollable.strategy == FIND_STRATEGY_XPATH || # uiautomator cannot resolve scrollable from a xpath locator
|
33
|
-
|
34
|
-
|
31
|
+
!@deadzone.nil? ||
|
32
|
+
!@scrollable.from_element.instance_of?(TestaAppiumDriver::Driver) # uiautomator cannot resolve nested scrollable
|
35
33
|
@strategy = SCROLL_STRATEGY_W3C
|
36
34
|
end
|
37
35
|
|
38
|
-
|
39
36
|
@bounds = @scrollable.bounds
|
40
37
|
end
|
41
38
|
|
42
|
-
|
43
39
|
def align(with, scroll_to_find)
|
44
40
|
w3c_align(with, scroll_to_find)
|
45
41
|
@locator
|
@@ -66,7 +62,6 @@ module TestaAppiumDriver
|
|
66
62
|
w3c_each(:left, &block)
|
67
63
|
end
|
68
64
|
|
69
|
-
|
70
65
|
def resolve_strategy
|
71
66
|
if @strategy.nil?
|
72
67
|
@default_scroll_strategy
|
@@ -75,7 +70,6 @@ module TestaAppiumDriver
|
|
75
70
|
end
|
76
71
|
end
|
77
72
|
|
78
|
-
|
79
73
|
def scroll_to
|
80
74
|
if @locator.strategy != FIND_STRATEGY_XPATH && resolve_strategy == SCROLL_STRATEGY_UIAUTOMATOR
|
81
75
|
uiautomator_scroll_to
|
@@ -116,7 +110,6 @@ module TestaAppiumDriver
|
|
116
110
|
end
|
117
111
|
end
|
118
112
|
|
119
|
-
|
120
113
|
def page_down
|
121
114
|
if resolve_strategy == SCROLL_STRATEGY_UIAUTOMATOR
|
122
115
|
uiautomator_page_or_fling(SCROLL_ACTION_TYPE_SCROLL, :down)
|
@@ -149,10 +142,10 @@ module TestaAppiumDriver
|
|
149
142
|
end
|
150
143
|
end
|
151
144
|
|
152
|
-
|
153
145
|
def scroll_to_start
|
154
146
|
if resolve_strategy == SCROLL_STRATEGY_UIAUTOMATOR
|
155
147
|
uiautomator_scroll_to_start_or_end(:start)
|
148
|
+
|
156
149
|
elsif resolve_strategy == SCROLL_STRATEGY_W3C
|
157
150
|
w3c_scroll_to_start_or_end(:start)
|
158
151
|
end
|
@@ -198,11 +191,10 @@ module TestaAppiumDriver
|
|
198
191
|
end
|
199
192
|
end
|
200
193
|
|
201
|
-
def drag_to(
|
202
|
-
w3c_drag_to(
|
194
|
+
def drag_to(x0, y0, x1, y1)
|
195
|
+
w3c_drag_to(x0, y0, x1, y1)
|
203
196
|
end
|
204
197
|
|
205
|
-
|
206
198
|
private
|
207
199
|
|
208
200
|
def is_end_of_scroll?
|
@@ -211,7 +203,6 @@ module TestaAppiumDriver
|
|
211
203
|
old_elements == @previous_elements
|
212
204
|
end
|
213
205
|
|
214
|
-
|
215
206
|
def default_deadzone!
|
216
207
|
@deadzone = {} if @deadzone.nil?
|
217
208
|
if @deadzone[:top].nil?
|
@@ -29,7 +29,6 @@ module TestaAppiumDriver
|
|
29
29
|
def initialize(opts = {})
|
30
30
|
@testa_opts = opts[:testa_appium_driver] || {}
|
31
31
|
|
32
|
-
|
33
32
|
core = Appium::Core.for(opts)
|
34
33
|
extend_for(core.device, core.automation_name)
|
35
34
|
@device = core.device
|
@@ -41,6 +40,9 @@ module TestaAppiumDriver
|
|
41
40
|
invalidate_cache
|
42
41
|
|
43
42
|
|
43
|
+
disable_wait_for_idle
|
44
|
+
disable_implicit_wait
|
45
|
+
|
44
46
|
Selenium::WebDriver::Element.set_driver(self, opts[:caps][:udid])
|
45
47
|
end
|
46
48
|
|
@@ -67,11 +69,9 @@ module TestaAppiumDriver
|
|
67
69
|
# @param [Array<Hash>] strategies_and_selectors array of usable strategies and selectors
|
68
70
|
# @param [Boolean] skip_cache to skip checking and storing cache
|
69
71
|
# @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
|
70
|
-
def execute(from_element, single, strategies_and_selectors, skip_cache
|
72
|
+
def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)
|
71
73
|
|
72
74
|
# if user wants to wait for element to exist, he can use wait_until_present
|
73
|
-
disable_wait_for_idle
|
74
|
-
disable_implicit_wait
|
75
75
|
start_time = Time.now.to_f
|
76
76
|
ss_index = 0
|
77
77
|
|
@@ -79,13 +79,13 @@ module TestaAppiumDriver
|
|
79
79
|
|
80
80
|
|
81
81
|
# resolve from_element unique id, so that we can cache it properly
|
82
|
-
from_element_id = from_element.
|
82
|
+
from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil
|
83
83
|
|
84
84
|
begin
|
85
85
|
ss = strategies_and_selectors[ss_index % strategies_and_selectors.count]
|
86
86
|
ss_index +=1
|
87
87
|
|
88
|
-
puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.
|
88
|
+
puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.strategies_and_selectors} => " : ""}#{ss.keys[0]}: #{ss.values[0]}"
|
89
89
|
|
90
90
|
if @cache[:selector] != ss.values[0] || # cache miss, selector is different
|
91
91
|
@cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
|
@@ -93,13 +93,24 @@ module TestaAppiumDriver
|
|
93
93
|
@cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
|
94
94
|
skip_cache # cache is skipped
|
95
95
|
|
96
|
-
if
|
97
|
-
|
96
|
+
if ss.keys[0] == FIND_STRATEGY_IMAGE
|
97
|
+
set_find_by_image_settings(ss.values[0].dup)
|
98
|
+
if single
|
99
|
+
execute_result = from_element.find_element_by_image(ss.values[0][:image])
|
100
|
+
else
|
101
|
+
execute_result = from_element.find_elements_by_image(ss.values[0][:image])
|
102
|
+
end
|
103
|
+
restore_set_by_image_settings
|
98
104
|
else
|
99
|
-
|
105
|
+
if single
|
106
|
+
execute_result = from_element.find_element(ss)
|
107
|
+
else
|
108
|
+
execute_result = from_element.find_elements(ss)
|
109
|
+
end
|
100
110
|
end
|
101
111
|
|
102
112
|
|
113
|
+
|
103
114
|
unless skip_cache
|
104
115
|
@cache[:selector] = ss.values[0]
|
105
116
|
@cache[:strategy] = ss.keys[0]
|
@@ -113,15 +124,13 @@ module TestaAppiumDriver
|
|
113
124
|
puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
|
114
125
|
end
|
115
126
|
rescue => e
|
116
|
-
if start_time + @implicit_wait_ms/1000 < Time.now.to_f || ss_index < strategies_and_selectors.count
|
127
|
+
#if (start_time + @implicit_wait_ms/1000 < Time.now.to_f && !ignore_implicit_wait) || ss_index < strategies_and_selectors.count
|
128
|
+
if ss_index < strategies_and_selectors.count
|
117
129
|
sleep EXISTS_WAIT if ss_index >= strategies_and_selectors.count
|
118
130
|
retry
|
119
131
|
else
|
120
132
|
raise e
|
121
133
|
end
|
122
|
-
ensure
|
123
|
-
enable_implicit_wait
|
124
|
-
enable_wait_for_idle
|
125
134
|
end
|
126
135
|
|
127
136
|
execute_result
|
@@ -131,22 +140,20 @@ module TestaAppiumDriver
|
|
131
140
|
# method missing is used to forward methods to the actual appium driver
|
132
141
|
# after the method is executed, find element cache is invalidated
|
133
142
|
def method_missing(method, *args, &block)
|
143
|
+
r = @driver.send(method, *args, &block)
|
134
144
|
invalidate_cache
|
135
|
-
|
145
|
+
r
|
136
146
|
end
|
137
147
|
|
138
148
|
# disables implicit wait
|
139
149
|
def disable_implicit_wait
|
140
|
-
|
141
|
-
|
150
|
+
@implicit_wait_ms = @driver.get_timeouts["implicit"].to_i
|
151
|
+
@implicit_wait_ms = @implicit_wait_ms/1000 if @implicit_wait_ms > 100000
|
152
|
+
@implicit_wait_uiautomator_ms = @driver.get_settings["waitForSelectorTimeout"]
|
153
|
+
@driver.manage.timeouts.implicit_wait = 0
|
154
|
+
@driver.update_settings({waitForSelectorTimeout: 0})
|
142
155
|
end
|
143
156
|
|
144
|
-
# enables implicit wait, can be called only after disabling implicit wait
|
145
|
-
def enable_implicit_wait
|
146
|
-
raise "Implicit wait is not disabled" if @implicit_wait_ms.nil?
|
147
|
-
# get_timeouts always returns in milliseconds, but we should set in seconds
|
148
|
-
@driver.manage.timeouts.implicit_wait = @implicit_wait_ms / 1000
|
149
|
-
end
|
150
157
|
|
151
158
|
# disables wait for idle, only executed for android devices
|
152
159
|
def disable_wait_for_idle
|
@@ -156,12 +163,26 @@ module TestaAppiumDriver
|
|
156
163
|
end
|
157
164
|
end
|
158
165
|
|
159
|
-
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
166
|
+
|
167
|
+
def set_find_by_image_settings(settings)
|
168
|
+
settings.delete(:image)
|
169
|
+
@default_find_image_settings = {}
|
170
|
+
old_settings = @driver.get_settings
|
171
|
+
@default_find_image_settings[:imageMatchThreshold] = old_settings["imageMatchThreshold"]
|
172
|
+
@default_find_image_settings[:fixImageFindScreenshotDims] = old_settings["fixImageFindScreenshotDims"]
|
173
|
+
@default_find_image_settings[:fixImageTemplateSize] = old_settings["fixImageTemplateSize"]
|
174
|
+
@default_find_image_settings[:fixImageTemplateScale] = old_settings["fixImageTemplateScale"]
|
175
|
+
@default_find_image_settings[:defaultImageTemplateScale] = old_settings["defaultImageTemplateScale"]
|
176
|
+
@default_find_image_settings[:checkForImageElementStaleness] = old_settings["checkForImageElementStaleness"]
|
177
|
+
@default_find_image_settings[:autoUpdateImageElementPosition] = old_settings["autoUpdateImageElementPosition"]
|
178
|
+
@default_find_image_settings[:imageElementTapStrategy] = old_settings["imageElementTapStrategy"]
|
179
|
+
@default_find_image_settings[:getMatchedImageResult] = old_settings["getMatchedImageResult"]
|
180
|
+
|
181
|
+
@driver.update_settings(settings)
|
182
|
+
end
|
183
|
+
|
184
|
+
def restore_set_by_image_settings
|
185
|
+
@driver.update_settings(@default_find_image_settings) if @default_find_image_settings
|
165
186
|
end
|
166
187
|
|
167
188
|
|
@@ -188,6 +209,33 @@ module TestaAppiumDriver
|
|
188
209
|
@driver.hide_keyboard
|
189
210
|
end
|
190
211
|
|
212
|
+
def home_key
|
213
|
+
@driver.press_keycode(3)
|
214
|
+
end
|
215
|
+
def tab_key
|
216
|
+
@driver.press_keycode(61)
|
217
|
+
end
|
218
|
+
|
219
|
+
def dpad_up_key
|
220
|
+
@driver.press_keycode(19)
|
221
|
+
end
|
222
|
+
|
223
|
+
def dpad_down_key
|
224
|
+
@driver.press_keycode(20)
|
225
|
+
end
|
226
|
+
|
227
|
+
def dpad_right_key
|
228
|
+
@driver.press_keycode(22)
|
229
|
+
end
|
230
|
+
|
231
|
+
def dpad_left_key
|
232
|
+
@driver.press_keycode(23)
|
233
|
+
end
|
234
|
+
|
235
|
+
def enter_key
|
236
|
+
@driver.press_keycode(66)
|
237
|
+
end
|
238
|
+
|
191
239
|
def press_keycode(code)
|
192
240
|
@driver.press_keycode(code)
|
193
241
|
end
|
@@ -197,13 +245,11 @@ module TestaAppiumDriver
|
|
197
245
|
end
|
198
246
|
|
199
247
|
|
248
|
+
|
249
|
+
|
200
250
|
# @return [Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page
|
201
251
|
def first_and_last_leaf(from_element = @driver)
|
202
|
-
disable_wait_for_idle
|
203
|
-
disable_implicit_wait
|
204
252
|
elements = from_element.find_elements(xpath: "//*[not(*)]")
|
205
|
-
enable_implicit_wait
|
206
|
-
enable_wait_for_idle
|
207
253
|
return nil if elements.count == 0
|
208
254
|
[elements[0], elements[-1]]
|
209
255
|
end
|
@@ -7,6 +7,19 @@ module TestaAppiumDriver
|
|
7
7
|
include TypeSelectors
|
8
8
|
|
9
9
|
|
10
|
+
|
11
|
+
# @param params [Hash]
|
12
|
+
# @return [TestaAppiumDriver::Locator] first scrollable element
|
13
|
+
def scrollable(params = {})
|
14
|
+
scroll_view(params)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param params [Hash]
|
18
|
+
# @return [TestaAppiumDriver::Locator] first scrollable element
|
19
|
+
def scrollables(params = {})
|
20
|
+
scroll_views(params)
|
21
|
+
end
|
22
|
+
|
10
23
|
private
|
11
24
|
def handle_testa_opts
|
12
25
|
if @testa_opts[:default_find_strategy].nil?
|
@@ -3,6 +3,7 @@ require_relative 'locator/attributes'
|
|
3
3
|
module TestaAppiumDriver
|
4
4
|
class Locator
|
5
5
|
include TypeSelectors
|
6
|
+
attr_accessor :class_chain_selector
|
6
7
|
|
7
8
|
def init(params, selectors, single)
|
8
9
|
if is_scrollable_selector?(selectors, single)
|
@@ -19,6 +20,9 @@ module TestaAppiumDriver
|
|
19
20
|
params[:scrollable_locator] = self.dup
|
20
21
|
end
|
21
22
|
|
23
|
+
@class_chain_selector = hash_to_class_chain(selectors, single)
|
24
|
+
|
25
|
+
|
22
26
|
@scrollable_locator = params[:scrollable_locator] if params[:scrollable_locator]
|
23
27
|
end
|
24
28
|
|
@@ -29,7 +33,9 @@ module TestaAppiumDriver
|
|
29
33
|
if @can_use_id_strategy
|
30
34
|
ss.push({"#{FIND_STRATEGY_NAME}": @can_use_id_strategy})
|
31
35
|
end
|
32
|
-
ss.push({"#{
|
36
|
+
ss.push({"#{FIND_STRATEGY_CLASS_CHAIN}": @class_chain_selector}) if @strategy.nil? || @strategy == FIND_STRATEGY_CLASS_CHAIN
|
37
|
+
ss.push({"#{FIND_STRATEGY_XPATH}": @xpath_selector}) if @strategy.nil? || @strategy == FIND_STRATEGY_XPATH
|
38
|
+
ss.push({"#{FIND_STRATEGY_IMAGE}": @image_selector}) if @strategy == FIND_STRATEGY_IMAGE
|
33
39
|
ss
|
34
40
|
end
|
35
41
|
|
@@ -43,13 +49,23 @@ module TestaAppiumDriver
|
|
43
49
|
|
44
50
|
locator = self.dup
|
45
51
|
add_xpath_child_selectors(locator, selectors, single)
|
52
|
+
if @strategy.nil? || @strategy == FIND_STRATEGY_CLASS_CHAIN
|
53
|
+
add_class_chain_child_selectors(locator, selectors, single)
|
54
|
+
end
|
55
|
+
|
46
56
|
if is_scrollable_selector?(selectors, single)
|
47
57
|
locator.scrollable_locator.scroll_orientation = :vertical
|
48
|
-
locator.scrollable_locator =
|
58
|
+
locator.scrollable_locator = locator
|
49
59
|
end
|
50
60
|
|
51
61
|
locator.last_selector_adjacent = false
|
52
62
|
locator
|
53
63
|
end
|
64
|
+
|
65
|
+
|
66
|
+
def add_class_chain_child_selectors(locator, selectors, single)
|
67
|
+
locator.single = false unless single # switching from single result to multiple
|
68
|
+
locator.class_chain_selector += "/" + hash_to_class_chain(selectors, single)
|
69
|
+
end
|
54
70
|
end
|
55
71
|
end
|
@@ -5,10 +5,15 @@ module TestaAppiumDriver
|
|
5
5
|
# @return [TestaAppiumDriver::Locator]
|
6
6
|
def add_selector(*args, &block)
|
7
7
|
# if class selector is executed from driver, create new locator instance
|
8
|
-
if self.kind_of?(TestaAppiumDriver::Driver) || self.
|
8
|
+
if self.kind_of?(TestaAppiumDriver::Driver) || self.instance_of?(Selenium::WebDriver::Element)
|
9
9
|
args.last[:default_find_strategy] = @default_find_strategy
|
10
10
|
args.last[:default_scroll_strategy] = @default_scroll_strategy
|
11
|
-
|
11
|
+
if self.instance_of?(Selenium::WebDriver::Element)
|
12
|
+
driver = self.get_driver
|
13
|
+
else
|
14
|
+
driver = self
|
15
|
+
end
|
16
|
+
Locator.new(driver, self, *args)
|
12
17
|
else
|
13
18
|
# class selector is executed from locator, just add child selector criteria
|
14
19
|
self.add_child_selector(*args)
|
@@ -111,19 +116,6 @@ module TestaAppiumDriver
|
|
111
116
|
end
|
112
117
|
|
113
118
|
|
114
|
-
# @param params [Hash]
|
115
|
-
# @return [TestaAppiumDriver::Locator] first scrollable element
|
116
|
-
def scrollable(params = {})
|
117
|
-
scroll_view(params)
|
118
|
-
end
|
119
|
-
|
120
|
-
# @param params [Hash]
|
121
|
-
# @return [TestaAppiumDriver::Locator] first scrollable element
|
122
|
-
def scrollables(params = {})
|
123
|
-
scroll_views(params)
|
124
|
-
end
|
125
|
-
|
126
|
-
|
127
119
|
# @return [TestaAppiumDriver::Locator]
|
128
120
|
def scroll_view(params = {})
|
129
121
|
params[:type] = "XCUIElementTypeScrollView"
|
@@ -163,5 +155,34 @@ module TestaAppiumDriver
|
|
163
155
|
params[:single] = false
|
164
156
|
add_selector(params)
|
165
157
|
end
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
# @return [TestaAppiumDriver::Locator]
|
162
|
+
def text_field(params = {})
|
163
|
+
params[:type] = "XCUIElementTypeTextField"
|
164
|
+
add_selector(params)
|
165
|
+
end
|
166
|
+
|
167
|
+
# @return [TestaAppiumDriver::Locator]
|
168
|
+
def text_fields(params = {})
|
169
|
+
params[:type] = "XCUIElementTypeTextField"
|
170
|
+
params[:single] = false
|
171
|
+
add_selector(params)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
# @return [TestaAppiumDriver::Locator]
|
176
|
+
def secure_text_field(params = {})
|
177
|
+
params[:type] = "XCUIElementTypeSecureTextField"
|
178
|
+
add_selector(params)
|
179
|
+
end
|
180
|
+
|
181
|
+
# @return [TestaAppiumDriver::Locator]
|
182
|
+
def secure_text_fields(params = {})
|
183
|
+
params[:type] = "XCUIElementTypeSecureTextField"
|
184
|
+
params[:single] = false
|
185
|
+
add_selector(params)
|
186
|
+
end
|
166
187
|
end
|
167
188
|
end
|
data/testa_appium_driver.iml
CHANGED
@@ -11,12 +11,12 @@
|
|
11
11
|
<orderEntry type="sourceFolder" forTests="false" />
|
12
12
|
<orderEntry type="library" scope="PROVIDED" name="appium_lib_core (v4.7.0, ruby-2.6.5-p114) [gem]" level="application" />
|
13
13
|
<orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, ruby-2.6.5-p114) [gem]" level="application" />
|
14
|
-
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.
|
14
|
+
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.1.4, ruby-2.6.5-p114) [gem]" level="application" />
|
15
15
|
<orderEntry type="library" scope="PROVIDED" name="childprocess (v3.0.0, ruby-2.6.5-p114) [gem]" level="application" />
|
16
16
|
<orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.4.4, ruby-2.6.5-p114) [gem]" level="application" />
|
17
17
|
<orderEntry type="library" scope="PROVIDED" name="eventmachine (v1.2.7, ruby-2.6.5-p114) [gem]" level="application" />
|
18
18
|
<orderEntry type="library" scope="PROVIDED" name="faye-websocket (v0.11.1, ruby-2.6.5-p114) [gem]" level="application" />
|
19
|
-
<orderEntry type="library" scope="PROVIDED" name="json (v2.
|
19
|
+
<orderEntry type="library" scope="PROVIDED" name="json (v2.1.0, ruby-2.6.5-p114) [gem]" level="application" />
|
20
20
|
<orderEntry type="library" scope="PROVIDED" name="parallel (v1.20.1, ruby-2.6.5-p114) [gem]" level="application" />
|
21
21
|
<orderEntry type="library" scope="PROVIDED" name="parser (v3.0.2.0, ruby-2.6.5-p114) [gem]" level="application" />
|
22
22
|
<orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, ruby-2.6.5-p114) [gem]" level="application" />
|
@@ -28,8 +28,8 @@
|
|
28
28
|
<orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.10.1, ruby-2.6.5-p114) [gem]" level="application" />
|
29
29
|
<orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.10.2, ruby-2.6.5-p114) [gem]" level="application" />
|
30
30
|
<orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.10.2, ruby-2.6.5-p114) [gem]" level="application" />
|
31
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop (v1.
|
32
|
-
<orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.
|
31
|
+
<orderEntry type="library" scope="PROVIDED" name="rubocop (v1.19.0, ruby-2.6.5-p114) [gem]" level="application" />
|
32
|
+
<orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.10.0, ruby-2.6.5-p114) [gem]" level="application" />
|
33
33
|
<orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, ruby-2.6.5-p114) [gem]" level="application" />
|
34
34
|
<orderEntry type="library" scope="PROVIDED" name="rubyzip (v2.3.2, ruby-2.6.5-p114) [gem]" level="application" />
|
35
35
|
<orderEntry type="library" scope="PROVIDED" name="selenium-webdriver (v3.142.7, ruby-2.6.5-p114) [gem]" level="application" />
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testa_appium_driver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- karlo.razumovic
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appium_lib_core
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- ".idea/inspectionProfiles/Project_Default.xml"
|
81
81
|
- ".idea/misc.xml"
|
82
82
|
- ".idea/modules.xml"
|
83
|
+
- ".idea/runConfigurations.xml"
|
83
84
|
- ".idea/runConfigurations/Android_Test.xml"
|
84
85
|
- ".idea/sshConfigs.xml"
|
85
86
|
- ".idea/vcs.xml"
|