testa_appium_driver 0.1.18 → 0.1.20

Sign up to get free protection for your applications and to get access to all the features.
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