testa_appium_driver 0.1.11 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +23 -0
  3. data/.gitignore +16 -16
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +13 -13
  6. data/CHANGELOG.md +5 -5
  7. data/CODE_OF_CONDUCT.md +102 -102
  8. data/Gemfile +12 -12
  9. data/Gemfile.lock +74 -0
  10. data/LICENSE.txt +21 -21
  11. data/README.md +402 -378
  12. data/Rakefile +12 -12
  13. data/{testa_appium_driver.iml → appium-driver.iml} +71 -78
  14. data/bin/console +17 -17
  15. data/bin/setup +8 -8
  16. data/lib/testa_appium_driver/android/class_selectors.rb +437 -437
  17. data/lib/testa_appium_driver/android/driver.rb +70 -69
  18. data/lib/testa_appium_driver/android/locator/attributes.rb +117 -113
  19. data/lib/testa_appium_driver/android/locator.rb +141 -141
  20. data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +61 -61
  21. data/lib/testa_appium_driver/android/selenium_element.rb +11 -7
  22. data/lib/testa_appium_driver/common/bounds.rb +149 -149
  23. data/lib/testa_appium_driver/common/constants.rb +37 -36
  24. data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +11 -11
  25. data/lib/testa_appium_driver/common/helpers.rb +270 -270
  26. data/lib/testa_appium_driver/common/locator/scroll_actions.rb +397 -397
  27. data/lib/testa_appium_driver/common/locator.rb +627 -610
  28. data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +3 -3
  29. data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +304 -237
  30. data/lib/testa_appium_driver/common/scroll_actions.rb +253 -246
  31. data/lib/testa_appium_driver/common/selenium_element.rb +19 -19
  32. data/lib/testa_appium_driver/driver.rb +328 -312
  33. data/lib/testa_appium_driver/ios/driver.rb +48 -48
  34. data/lib/testa_appium_driver/ios/locator/attributes.rb +84 -80
  35. data/lib/testa_appium_driver/ios/locator.rb +71 -70
  36. data/lib/testa_appium_driver/ios/selenium_element.rb +6 -6
  37. data/lib/testa_appium_driver/ios/type_selectors.rb +187 -187
  38. data/lib/testa_appium_driver/version.rb +5 -5
  39. data/lib/testa_appium_driver.rb +6 -6
  40. data/testa_appium_driver.gemspec +41 -41
  41. metadata +9 -16
  42. data/.idea/deployment.xml +0 -22
  43. data/.idea/inspectionProfiles/Project_Default.xml +0 -9
  44. data/.idea/misc.xml +0 -6
  45. data/.idea/modules.xml +0 -8
  46. data/.idea/runConfigurations/Android_Test.xml +0 -42
  47. data/.idea/runConfigurations.xml +0 -10
  48. data/.idea/sshConfigs.xml +0 -13
  49. data/.idea/vcs.xml +0 -6
  50. data/.idea/webServers.xml +0 -21
@@ -1,312 +1,328 @@
1
- # frozen_string_literal: true
2
-
3
- require 'em/pure_ruby'
4
- require 'appium_lib_core'
5
-
6
- require_relative 'common/bounds'
7
- require_relative 'common/exceptions/strategy_mix_exception'
8
- require_relative 'common/helpers'
9
- require_relative 'common/locator'
10
- require_relative 'common/scroll_actions'
11
- require_relative 'common/selenium_element'
12
-
13
- module TestaAppiumDriver
14
- class Driver
15
- include Helpers
16
-
17
- # @return [::Appium::Core::Base::Driver] the ruby_lib_core appium driver
18
- attr_accessor :driver
19
-
20
- # @return [String] iOS or Android
21
- attr_reader :device
22
-
23
- # @return [String] driver automation name (uiautomator2 or xcuitest)
24
- attr_reader :automation_name
25
-
26
- # custom options
27
- # - default_find_strategy: default strategy to be used for finding elements. Available strategies :uiautomator or :xpath
28
- # - default_scroll_strategy: default strategy to be used for scrolling. Available strategies: :uiautomator(android only), :w3c
29
- def initialize(opts = {})
30
- @testa_opts = opts[:testa_appium_driver] || {}
31
-
32
- core = Appium::Core.for(opts)
33
- extend_for(core.device, core.automation_name)
34
- @device = core.device
35
- @automation_name = core.automation_name
36
-
37
- handle_testa_opts
38
-
39
- @driver = core.start_driver
40
- invalidate_cache
41
-
42
-
43
- disable_wait_for_idle
44
- disable_implicit_wait
45
-
46
- Selenium::WebDriver::Element.set_driver(self, opts[:caps][:udid])
47
- end
48
-
49
-
50
- # invalidates current find_element cache
51
- def invalidate_cache
52
- @cache = {
53
- strategy: nil,
54
- selector: nil,
55
- element: nil,
56
- from_element: nil,
57
- time: Time.at(0)
58
- }
59
- end
60
-
61
-
62
-
63
-
64
- # Executes the find_element with the resolved locator strategy and selector. Find_element might be skipped if cache is hit.
65
- # Cache stores last executed find_element with given selector, strategy and from_element. If given values are the same within
66
- # last 5 seconds element is retrieved from cache.
67
- # @param [TestaAppiumDriver::Locator, TestaAppiumDriver::Driver] from_element element from which start the search
68
- # @param [Boolean] single fetch single or multiple results
69
- # @param [Array<Hash>] strategies_and_selectors array of usable strategies and selectors
70
- # @param [Boolean] skip_cache to skip checking and storing cache
71
- # @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
72
- def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)
73
-
74
- # if user wants to wait for element to exist, he can use wait_until_present
75
- start_time = Time.now.to_f
76
- ss_index = 0
77
-
78
-
79
-
80
-
81
- # resolve from_element unique id, so that we can cache it properly
82
- from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil
83
-
84
- begin
85
- ss = strategies_and_selectors[ss_index % strategies_and_selectors.count]
86
- ss_index +=1
87
-
88
- puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.strategies_and_selectors} => " : ""}#{ss.keys[0]}: #{ss.values[0]}"
89
-
90
- if @cache[:selector] != ss.values[0] || # cache miss, selector is different
91
- @cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
92
- @cache[:strategy] != ss.keys[0] || # cache miss, different find strategy
93
- @cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
94
- skip_cache # cache is skipped
95
-
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
104
- else
105
- if single
106
- execute_result = from_element.find_element(ss)
107
- else
108
- execute_result = from_element.find_elements(ss)
109
- end
110
- end
111
-
112
-
113
-
114
- unless skip_cache
115
- @cache[:selector] = ss.values[0]
116
- @cache[:strategy] = ss.keys[0]
117
- @cache[:time] = Time.now
118
- @cache[:from_element_id] = from_element_id
119
- @cache[:element] = execute_result
120
- end
121
- else
122
- # this is a cache hit, use the element from cache
123
- execute_result = @cache[:element]
124
- puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
125
- end
126
- rescue => e
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
129
- sleep EXISTS_WAIT if ss_index >= strategies_and_selectors.count
130
- retry
131
- else
132
- raise e
133
- end
134
- end
135
-
136
- execute_result
137
- end
138
-
139
-
140
- # method missing is used to forward methods to the actual appium driver
141
- # after the method is executed, find element cache is invalidated
142
- def method_missing(method, *args, &block)
143
- r = @driver.send(method, *args, &block)
144
- invalidate_cache
145
- r
146
- end
147
-
148
- # disables implicit wait
149
- def disable_implicit_wait
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: 1})
155
- end
156
-
157
-
158
- # disables wait for idle, only executed for android devices
159
- def disable_wait_for_idle
160
- if @device == :android
161
- @wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
162
- @driver.update_settings({waitForIdleTimeout: 1})
163
- end
164
- end
165
-
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
186
- end
187
-
188
-
189
- # @@return [String] current package under test
190
- def current_package
191
- @driver.current_package
192
- end
193
-
194
-
195
- def window_size
196
- @driver.window_size
197
- end
198
-
199
- def back
200
- @driver.back
201
- end
202
-
203
-
204
- def is_keyboard_shown?
205
- @driver.is_keyboard_shown
206
- end
207
-
208
- def hide_keyboard
209
- @driver.hide_keyboard
210
- end
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
-
239
- def press_keycode(code)
240
- @driver.press_keycode(code)
241
- end
242
-
243
- def long_press_keycode(code)
244
- @driver.long_press_keycode(code)
245
- end
246
-
247
- def click(x, y)
248
- ws = driver.window_size
249
- window_width = ws.width.to_i
250
- window_height = ws.height.to_i
251
- if x.kind_of? Integer
252
- if x < 0
253
- x = window_width + x
254
- end
255
- elsif x.kind_of? Float
256
- x = window_width*x
257
- else
258
- raise "x value #{x} not supported"
259
- end
260
-
261
- if y.kind_of? Integer
262
- if y < 0
263
- y = window_height + y
264
- end
265
- elsif y.kind_of? Float
266
- y = window_height*y
267
- end
268
-
269
-
270
- action_builder = @driver.action
271
- f1 = action_builder.add_pointer_input(:touch, "finger1")
272
- f1.create_pointer_move(duration: 0, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
273
- f1.create_pointer_down(:left)
274
- f1.create_pointer_up(:left)
275
- @driver.perform_actions [f1]
276
- end
277
-
278
-
279
-
280
- # @return [Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page
281
- def first_and_last_leaf(from_element = @driver)
282
- elements = from_element.find_elements(xpath: "//*[not(*)]")
283
- return nil if elements.count == 0
284
- [elements[0], elements[-1]]
285
- end
286
-
287
- private
288
- def extend_for(device, automation_name)
289
- case device
290
- when :android
291
- case automation_name
292
- when :uiautomator2
293
- require_relative 'android/driver'
294
- else
295
- raise "Testa appium driver not supported for #{automation_name} automation"
296
- end
297
- when :ios, :tvos
298
- case automation_name
299
- when :xcuitest
300
- require_relative 'ios/driver'
301
- else
302
- raise "Testa appium driver not supported for #{automation_name} automation"
303
- end
304
- else
305
- raise "Unknown device #{device}, should be either android, ios or tvos"
306
- end
307
- end
308
-
309
-
310
- end
311
-
312
- end
1
+ # frozen_string_literal: true
2
+
3
+ #require 'em/pure_ruby'
4
+ require 'appium_lib_core'
5
+
6
+ require_relative 'common/bounds'
7
+ require_relative 'common/exceptions/strategy_mix_exception'
8
+ require_relative 'common/helpers'
9
+ require_relative 'common/locator'
10
+ require_relative 'common/scroll_actions'
11
+ require_relative 'common/selenium_element'
12
+
13
+ module TestaAppiumDriver
14
+ class Driver
15
+ include Helpers
16
+
17
+ # @return [::Appium::Core::Base::Driver] the ruby_lib_core appium driver
18
+ attr_accessor :driver
19
+
20
+ # @return [String] iOS or Android
21
+ attr_reader :device
22
+
23
+ # @return [String] driver automation name (uiautomator2 or xcuitest)
24
+ attr_reader :automation_name
25
+
26
+ # custom options
27
+ # - default_find_strategy: default strategy to be used for finding elements. Available strategies :uiautomator or :xpath
28
+ # - default_scroll_strategy: default strategy to be used for scrolling. Available strategies: :uiautomator(android only), :w3c
29
+ def initialize(opts = {})
30
+
31
+ @testa_opts = opts[:testa_appium_driver] || {}
32
+
33
+ core = Appium::Core.for(opts)
34
+ @driver = core.start_driver
35
+ @automation_name = @driver.capabilities["automationName"].downcase.to_sym
36
+ @device = @driver.capabilities.platform_name.downcase.to_sym
37
+
38
+ extend_for(@device, @automation_name)
39
+
40
+ handle_testa_opts
41
+
42
+ invalidate_cache
43
+
44
+ #disable_wait_for_idle
45
+ #disable_implicit_wait
46
+
47
+ ::Appium::Core::Element.set_driver(self, @driver.capabilities["udid"])
48
+ end
49
+
50
+
51
+ # invalidates current find_element cache
52
+ def invalidate_cache
53
+ @cache = {
54
+ strategy: nil,
55
+ selector: nil,
56
+ element: nil,
57
+ from_element: nil,
58
+ time: Time.at(0)
59
+ }
60
+ end
61
+
62
+
63
+
64
+
65
+ # Executes the find_element with the resolved locator strategy and selector. Find_element might be skipped if cache is hit.
66
+ # Cache stores last executed find_element with given selector, strategy and from_element. If given values are the same within
67
+ # last 5 seconds element is retrieved from cache.
68
+ # @param [TestaAppiumDriver::Locator, TestaAppiumDriver::Driver] from_element element from which start the search
69
+ # @param [Boolean] single fetch single or multiple results
70
+ # @param [Array<Hash>] strategies_and_selectors array of usable strategies and selectors
71
+ # @param [Boolean] skip_cache to skip checking and storing cache
72
+ # @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
73
+ def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)
74
+
75
+ # if user wants to wait for element to exist, he can use wait_until_present
76
+ start_time = Time.now.to_f
77
+ ss_index = 0
78
+
79
+
80
+
81
+
82
+ # resolve from_element unique id, so that we can cache it properly
83
+ from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil
84
+
85
+ begin
86
+ begin
87
+ ss = strategies_and_selectors[ss_index % strategies_and_selectors.count]
88
+ rescue ZeroDivisionError
89
+ puts "aa"
90
+ end
91
+ ss_index +=1
92
+
93
+ puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.strategies_and_selectors} => " : ""}#{ss.keys[0]}: #{ss.values[0]}"
94
+
95
+ if @cache[:selector] != ss.values[0] || # cache miss, selector is different
96
+ @cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
97
+ @cache[:strategy] != ss.keys[0] || # cache miss, different find strategy
98
+ @cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
99
+ skip_cache # cache is skipped
100
+
101
+ if ss.keys[0] == FIND_STRATEGY_IMAGE
102
+ set_find_by_image_settings(ss.values[0].dup)
103
+ if single
104
+ execute_result = from_element.find_element_by_image(ss.values[0][:image])
105
+ else
106
+ execute_result = from_element.find_elements_by_image(ss.values[0][:image])
107
+ end
108
+ restore_set_by_image_settings
109
+ else
110
+ if single
111
+ execute_result = from_element.find_element(ss)
112
+ else
113
+ execute_result = from_element.find_elements(ss)
114
+ end
115
+ end
116
+
117
+
118
+
119
+ unless skip_cache
120
+ @cache[:selector] = ss.values[0]
121
+ @cache[:strategy] = ss.keys[0]
122
+ @cache[:time] = Time.now
123
+ @cache[:from_element_id] = from_element_id
124
+ @cache[:element] = execute_result
125
+ end
126
+ else
127
+ # this is a cache hit, use the element from cache
128
+ execute_result = @cache[:element]
129
+ puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
130
+ end
131
+ rescue => e
132
+ #if (start_time + @implicit_wait_ms/1000 < Time.now.to_f && !ignore_implicit_wait) || ss_index < strategies_and_selectors.count
133
+ if ss_index < strategies_and_selectors.count
134
+ sleep EXISTS_WAIT if ss_index >= strategies_and_selectors.count
135
+ retry
136
+ else
137
+ raise e
138
+ end
139
+ end
140
+
141
+ execute_result
142
+ end
143
+
144
+
145
+ # method missing is used to forward methods to the actual appium driver
146
+ # after the method is executed, find element cache is invalidated
147
+ def method_missing(method, *args, &block)
148
+ r = @driver.send(method, *args, &block)
149
+ invalidate_cache
150
+ r
151
+ end
152
+
153
+ # disables implicit wait
154
+ def disable_implicit_wait
155
+ @implicit_wait_ms = @driver.get_timeouts["implicit"].to_i
156
+ @implicit_wait_ms = @implicit_wait_ms/1000 if @implicit_wait_ms > 100000
157
+ @implicit_wait_uiautomator_ms = @driver.get_settings["waitForSelectorTimeout"]
158
+ @driver.manage.timeouts.implicit_wait = 0
159
+ @driver.update_settings({waitForSelectorTimeout: 0})
160
+ end
161
+
162
+
163
+ # disables wait for idle, only executed for android devices
164
+ def disable_wait_for_idle
165
+ if @device == :android
166
+ @wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
167
+ @driver.update_settings({waitForIdleTimeout: 0})
168
+ end
169
+ end
170
+
171
+
172
+ def set_find_by_image_settings(settings)
173
+ settings.delete(:image)
174
+ @default_find_image_settings = {}
175
+ old_settings = @driver.get_settings
176
+ @default_find_image_settings[:imageMatchThreshold] = old_settings["imageMatchThreshold"]
177
+ @default_find_image_settings[:fixImageFindScreenshotDims] = old_settings["fixImageFindScreenshotDims"]
178
+ @default_find_image_settings[:fixImageTemplateSize] = old_settings["fixImageTemplateSize"]
179
+ @default_find_image_settings[:fixImageTemplateScale] = old_settings["fixImageTemplateScale"]
180
+ @default_find_image_settings[:defaultImageTemplateScale] = old_settings["defaultImageTemplateScale"]
181
+ @default_find_image_settings[:checkForImageElementStaleness] = old_settings["checkForImageElementStaleness"]
182
+ @default_find_image_settings[:autoUpdateImageElementPosition] = old_settings["autoUpdateImageElementPosition"]
183
+ @default_find_image_settings[:imageElementTapStrategy] = old_settings["imageElementTapStrategy"]
184
+ @default_find_image_settings[:getMatchedImageResult] = old_settings["getMatchedImageResult"]
185
+
186
+ @driver.update_settings(settings)
187
+ end
188
+
189
+ def restore_set_by_image_settings
190
+ @driver.update_settings(@default_find_image_settings) if @default_find_image_settings
191
+ end
192
+
193
+
194
+ # @@return [String] current package under test
195
+ def current_package
196
+ @driver.current_package
197
+ end
198
+
199
+
200
+ def window_size
201
+ @driver.window_size
202
+ end
203
+
204
+ def back
205
+ @driver.back
206
+ end
207
+
208
+
209
+ def is_keyboard_shown?
210
+ @driver.is_keyboard_shown
211
+ end
212
+
213
+ def hide_keyboard
214
+ @driver.hide_keyboard
215
+ end
216
+
217
+ def home_key
218
+ @driver.press_keycode(3)
219
+ end
220
+ def tab_key
221
+ @driver.press_keycode(61)
222
+ end
223
+
224
+ def dpad_up_key
225
+ @driver.press_keycode(19)
226
+ end
227
+
228
+ def dpad_down_key
229
+ @driver.press_keycode(20)
230
+ end
231
+
232
+ def dpad_right_key
233
+ @driver.press_keycode(22)
234
+ end
235
+
236
+ def dpad_left_key
237
+ @driver.press_keycode(23)
238
+ end
239
+
240
+ def enter_key
241
+ @driver.press_keycode(66)
242
+ end
243
+
244
+ def press_keycode(code)
245
+ @driver.press_keycode(code)
246
+ end
247
+
248
+ def long_press_keycode(code)
249
+ @driver.long_press_keycode(code)
250
+ end
251
+
252
+ def click(x, y, double: false)
253
+ ws = driver.window_size
254
+ window_width = ws.width.to_i
255
+ window_height = ws.height.to_i
256
+ if x.kind_of?(Integer)
257
+ if x < 0
258
+ x = window_width + x
259
+ end
260
+ elsif x.kind_of?(Float) && x <= 1.0 && x >= 0
261
+ x = window_width*x
262
+ else
263
+ raise "x value #{x} not supported. Use integer as pixel or float (0..1) as percentage of screen"
264
+ end
265
+
266
+ if y.kind_of?(Integer)
267
+ if y < 0
268
+ y = window_height + y
269
+ end
270
+ elsif y.kind_of?(Float) && y <= 1.0 && y >= 0
271
+ y = window_height*y
272
+ else
273
+ raise "y value #{x} not supported. Use integer as pixel or float (0..1) as percentage of screen"
274
+ end
275
+
276
+
277
+ action_builder = @driver.action
278
+ f1 = action_builder.add_pointer_input(:touch, "finger1")
279
+ f1.create_pointer_move(duration: 0, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
280
+ f1.create_pointer_down(:left)
281
+ f1.create_pointer_up(:left)
282
+ if double
283
+ f1.create_pause(0.1)
284
+ f1.create_pointer_down(:left)
285
+ f1.create_pointer_up(:left)
286
+ end
287
+ @driver.perform_actions [f1]
288
+ end
289
+
290
+ def double_click(x,y)
291
+ click(x,y, double: true)
292
+ end
293
+
294
+
295
+
296
+ # @return [Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page
297
+ def first_and_last_leaf(from_element = @driver)
298
+ elements = from_element.find_elements(xpath: ".//*[not(*)]")
299
+ return nil if elements.count == 0
300
+ [elements[0], elements[-1]]
301
+ end
302
+
303
+ private
304
+ def extend_for(device, automation_name)
305
+ case device
306
+ when :android
307
+ case automation_name
308
+ when :uiautomator2, :espresso, :Espresso
309
+ require_relative 'android/driver'
310
+ else
311
+ raise "Testa appium driver not supported for #{automation_name} automation"
312
+ end
313
+ when :ios, :tvos
314
+ case automation_name
315
+ when :xcuitest
316
+ require_relative 'ios/driver'
317
+ else
318
+ raise "Testa appium driver not supported for #{automation_name} automation"
319
+ end
320
+ else
321
+ raise "Unknown device #{device}, should be either android, ios or tvos"
322
+ end
323
+ end
324
+
325
+
326
+ end
327
+
328
+ end