testa_appium_driver 0.1.18 → 0.1.20

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +5 -1
  4. data/appium-driver.iml +79 -0
  5. data/testa_appium_driver.gemspec +4 -8
  6. metadata +11 -40
  7. data/.gitattributes +0 -23
  8. data/.gitignore +0 -17
  9. data/.rspec +0 -3
  10. data/.rubocop.yml +0 -5
  11. data/bin/console +0 -17
  12. data/bin/setup +0 -8
  13. data/lib/testa_appium_driver/android/class_selectors.rb +0 -438
  14. data/lib/testa_appium_driver/android/driver.rb +0 -71
  15. data/lib/testa_appium_driver/android/locator/attributes.rb +0 -118
  16. data/lib/testa_appium_driver/android/locator.rb +0 -142
  17. data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +0 -62
  18. data/lib/testa_appium_driver/android/selenium_element.rb +0 -12
  19. data/lib/testa_appium_driver/common/bounds.rb +0 -150
  20. data/lib/testa_appium_driver/common/constants.rb +0 -38
  21. data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +0 -12
  22. data/lib/testa_appium_driver/common/helpers.rb +0 -271
  23. data/lib/testa_appium_driver/common/locator/scroll_actions.rb +0 -398
  24. data/lib/testa_appium_driver/common/locator.rb +0 -599
  25. data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +0 -4
  26. data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +0 -305
  27. data/lib/testa_appium_driver/common/scroll_actions.rb +0 -267
  28. data/lib/testa_appium_driver/common/selenium_element.rb +0 -19
  29. data/lib/testa_appium_driver/driver.rb +0 -335
  30. data/lib/testa_appium_driver/ios/driver.rb +0 -49
  31. data/lib/testa_appium_driver/ios/locator/attributes.rb +0 -85
  32. data/lib/testa_appium_driver/ios/locator.rb +0 -72
  33. data/lib/testa_appium_driver/ios/selenium_element.rb +0 -8
  34. data/lib/testa_appium_driver/ios/type_selectors.rb +0 -188
  35. data/lib/testa_appium_driver/version.rb +0 -5
  36. data/lib/testa_appium_driver.rb +0 -6
@@ -1,335 +0,0 @@
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
- def first_and_last_child(from_element = @driver)
304
- elements = from_element.find_elements(xpath: "./*")
305
- return nil if elements.count == 0
306
-
307
- [elements[0], elements[-1]]
308
- end
309
-
310
- private
311
- def extend_for(device, automation_name)
312
- case device
313
- when :android
314
- case automation_name
315
- when :uiautomator2, :espresso, :Espresso
316
- require_relative 'android/driver'
317
- else
318
- raise "Testa appium driver not supported for #{automation_name} automation"
319
- end
320
- when :ios, :tvos
321
- case automation_name
322
- when :xcuitest
323
- require_relative 'ios/driver'
324
- else
325
- raise "Testa appium driver not supported for #{automation_name} automation"
326
- end
327
- else
328
- raise "Unknown device #{device}, should be either android, ios or tvos"
329
- end
330
- end
331
-
332
-
333
- end
334
-
335
- end
@@ -1,49 +0,0 @@
1
- require_relative 'type_selectors'
2
- require_relative 'locator'
3
- require_relative 'selenium_element'
4
-
5
- module TestaAppiumDriver
6
- class Driver
7
- include TypeSelectors
8
-
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
-
23
- private
24
- def handle_testa_opts
25
- if @testa_opts[:default_find_strategy].nil?
26
- @default_find_strategy = DEFAULT_IOS_FIND_STRATEGY
27
- else
28
- case @testa_opts[:default_find_strategy].to_sym
29
- when FIND_STRATEGY_XPATH
30
- @default_find_strategy = @testa_opts[:default_find_strategy].to_sym
31
- else
32
- raise "Default find strategy #{@testa_opts[:default_find_strategy]} not supported for iOS"
33
- end
34
- end
35
-
36
-
37
- if @testa_opts[:default_scroll_strategy].nil?
38
- @default_scroll_strategy = DEFAULT_IOS_SCROLL_STRATEGY
39
- else
40
- case @testa_opts[:default_scroll_strategy].to_sym
41
- when SCROLL_STRATEGY_W3C
42
- @default_scroll_strategy = @testa_opts[:default_scroll_strategy].to_sym
43
- else
44
- raise "Default scroll strategy #{@testa_opts[:default_scroll_strategy]} not supported for iOS"
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,85 +0,0 @@
1
- module TestaAppiumDriver
2
- module Attributes
3
-
4
- #noinspection RubyNilAnalysis
5
- def testa_attribute(name, *args)
6
-
7
- if self.instance_of?(::Selenium::WebDriver::Element) || self.instance_of?(::Appium::Core::Element)
8
- @driver = get_driver
9
- elements = self
10
- else
11
- elements = execute(*args)
12
- end
13
-
14
-
15
- if elements.instance_of?(::Selenium::WebDriver::Element) || elements.instance_of?(::Appium::Core::Element)
16
- r = elements.send(:attribute, name.to_s)
17
- r = TestaAppiumDriver::Bounds.from_ios(r, @driver) if name.to_s == "rect"
18
- else
19
- r = elements.map { |e| e.send(:attribute, name.to_s) }
20
- r.map! { |b| TestaAppiumDriver::Bounds.from_ios(b, @driver) } if name.to_s == "rect"
21
- end
22
- r
23
- end
24
-
25
-
26
- def accessibility_container(*args)
27
- testa_attribute("accessibilityContainer", *args)
28
- end
29
-
30
- def accessible?(*args)
31
- testa_attribute("accessible", *args).to_s == "true"
32
- end
33
-
34
-
35
- def class_name(*args)
36
- testa_attribute("class", *args)
37
- end
38
-
39
- def enabled?(*args)
40
- testa_attribute("enabled", *args).to_s == "true"
41
- end
42
-
43
- def frame(*args)
44
- testa_attribute("frame", *args)
45
- end
46
-
47
- def index(*args)
48
- testa_attribute("index", *args)
49
- end
50
-
51
- def label(*args)
52
- testa_attribute("label", *args)
53
- end
54
-
55
- def name(*args)
56
- testa_attribute("name", *args)
57
- end
58
-
59
-
60
- def rect(*args)
61
- testa_attribute("rect", *args)
62
- end
63
-
64
- def selected?(*args)
65
- testa_attribute("selected", *args).to_s == "true"
66
- end
67
-
68
- def type(*args)
69
- testa_attribute("type", *args)
70
- end
71
-
72
- def value(*args)
73
- testa_attribute("value", *args)
74
- end
75
-
76
- def visible?(*args)
77
- testa_attribute("visible", *args).to_s == "true"
78
- end
79
-
80
-
81
- alias_method :bounds, :rect
82
- alias_method :text, :label
83
- end
84
-
85
- end
@@ -1,72 +0,0 @@
1
- require_relative 'locator/attributes'
2
-
3
- module TestaAppiumDriver
4
- class Locator
5
- include TypeSelectors
6
- include Attributes
7
- attr_accessor :class_chain_selector
8
-
9
- def init(params, selectors, single)
10
- if is_scrollable_selector?(selectors, single)
11
- @scroll_orientation = :vertical
12
-
13
- if !params[:top].nil? || !params[:bottom].nil? || !params[:right].nil? || !params[:left].nil?
14
- @scroll_deadzone = {}
15
- @scroll_deadzone[:top] = params[:top].to_f unless params[:top].nil?
16
- @scroll_deadzone[:bottom] = params[:bottom].to_f unless params[:bottom].nil?
17
- @scroll_deadzone[:right] = params[:right].to_f unless params[:right].nil?
18
- @scroll_deadzone[:left] = params[:left].to_f unless params[:left].nil?
19
- end
20
-
21
- params[:scrollable_locator] = self.dup
22
- end
23
-
24
- @class_chain_selector = hash_to_class_chain(selectors, single)
25
-
26
-
27
- @scrollable_locator = params[:scrollable_locator] if params[:scrollable_locator]
28
- end
29
-
30
-
31
- # @return [Array] returns 2 elements. The first is the resolved find element strategy and the second is the resolved selector
32
- def strategies_and_selectors
33
- ss = []
34
- if @can_use_id_strategy
35
- ss.push({"#{FIND_STRATEGY_NAME}": @can_use_id_strategy})
36
- end
37
- ss.push({"#{FIND_STRATEGY_CLASS_CHAIN}": @class_chain_selector}) if @strategy.nil? || @strategy == FIND_STRATEGY_CLASS_CHAIN
38
- ss.push({"#{FIND_STRATEGY_XPATH}": @xpath_selector}) if @strategy.nil? || @strategy == FIND_STRATEGY_XPATH
39
- ss.push({"#{FIND_STRATEGY_IMAGE}": @image_selector}) if @strategy == FIND_STRATEGY_IMAGE
40
- ss
41
- end
42
-
43
-
44
-
45
- # @return [Locator] new child locator element
46
- def add_child_selector(params)
47
- params, selectors = extract_selectors_from_params(params)
48
- single = params[:single]
49
- raise "Cannot add child selector to Array" if single && !@single
50
-
51
- locator = self.dup
52
- add_xpath_child_selectors(locator, selectors, single)
53
- if @strategy.nil? || @strategy == FIND_STRATEGY_CLASS_CHAIN
54
- add_class_chain_child_selectors(locator, selectors, single)
55
- end
56
-
57
- if is_scrollable_selector?(selectors, single)
58
- locator.scrollable_locator.scroll_orientation = :vertical
59
- locator.scrollable_locator = locator
60
- end
61
-
62
- locator.last_selector_adjacent = false
63
- locator
64
- end
65
-
66
-
67
- def add_class_chain_child_selectors(locator, selectors, single)
68
- locator.single = false unless single # switching from single result to multiple
69
- locator.class_chain_selector += "/" + hash_to_class_chain(selectors, single)
70
- end
71
- end
72
- end
@@ -1,8 +0,0 @@
1
- module ::Appium
2
- module Core
3
- class Element
4
- include TestaAppiumDriver::TypeSelectors
5
- include TestaAppiumDriver::Attributes
6
- end
7
- end
8
- end