testa_appium_driver 0.1.5 → 0.1.9

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.
@@ -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
@@ -25,6 +27,8 @@ module TestaAppiumDriver
25
27
  attr_accessor :default_find_strategy
26
28
  attr_accessor :default_scroll_strategy
27
29
 
30
+ attr_accessor :index_for_multiple
31
+
28
32
 
29
33
  # locator parameters are:
30
34
  # single: true or false
@@ -38,12 +42,22 @@ module TestaAppiumDriver
38
42
  def initialize(driver, from_element, params = {})
39
43
  # @type [TestaAppiumDriver::Driver]
40
44
  @driver = driver
45
+ @index_for_multiple = nil
46
+ @image_selector = nil
41
47
 
42
48
  params, selectors = extract_selectors_from_params(params)
43
49
  single = params[:single]
44
50
 
45
51
  @single = single
46
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
+
47
61
  selectors[:id] = selectors[:name] unless selectors[:name].nil?
48
62
  if from_element.instance_of?(Selenium::WebDriver::Element)
49
63
  @xpath_selector = "//*" # to select current element
@@ -57,7 +71,8 @@ module TestaAppiumDriver
57
71
  @default_find_strategy = params[:default_find_strategy]
58
72
  @default_scroll_strategy = params[:default_scroll_strategy]
59
73
 
60
- @can_use_id_strategy = selectors.keys.count == 1 && !selectors[:id].nil?
74
+
75
+ @can_use_id_strategy = is_only_id_selector?(selectors)
61
76
  if @can_use_id_strategy
62
77
  if @driver.device == :android
63
78
  @can_use_id_strategy = resolve_id(selectors[:id])
@@ -76,6 +91,19 @@ module TestaAppiumDriver
76
91
  end
77
92
 
78
93
 
94
+
95
+
96
+ def is_only_id_selector?(selectors)
97
+ # since, name and id is the same thing for iOS,
98
+ if @driver.device == :android
99
+ selectors.keys.count == 1 && !selectors[:id].nil?
100
+ else
101
+ # if it iOS we assign the name to id
102
+ selectors.keys.count == 2 && !selectors[:id].nil? && selectors[:id] == selectors[:name]
103
+ end
104
+ end
105
+
106
+
79
107
  # method missing is used to fetch the element before executing additional commands like click, send_key, count
80
108
  def method_missing(method, *args, &block)
81
109
  r = execute.send(method, *args, &block)
@@ -87,23 +115,46 @@ module TestaAppiumDriver
87
115
  # @param [Boolean] skip_cache if true it will skip cache check and store
88
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
89
117
  # @return [Selenium::WebDriver::Element, Array]
90
- def execute(skip_cache: false, force_cache_element: nil)
118
+ def execute(skip_cache: false, force_cache_element: nil, ignore_implicit_wait: false)
91
119
  return force_cache_element unless force_cache_element.nil?
92
120
  # if we are looking for current element, then return from_element
93
121
  # for example when we have driver.element.elements[1].click
94
122
  # elements[2] will be resolved with xpath because we are looking for multiple elements from element
95
123
  # and since we are looking for instance 2, [](instance) method will return new "empty locator"
96
124
  # we are executing click on that "empty locator" so we have to return the instance 2 of elements for the click
97
- if @xpath_selector == "//*/*[1]" && @from_element.instance_of?(Selenium::WebDriver::Element)
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)
98
128
  return @from_element
99
129
  end
100
130
 
101
131
 
102
132
 
103
- strategy, selector = strategy_and_selector
104
133
 
105
134
 
106
- @driver.execute(@from_element, selector, @single, strategy, @default_find_strategy, skip_cache)
135
+
136
+ r = @driver.execute(@from_element, @single, strategies_and_selectors, skip_cache: skip_cache, ignore_implicit_wait: ignore_implicit_wait)
137
+ r = r[@index_for_multiple] if !@index_for_multiple.nil? && !@single
138
+ r
139
+ end
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
107
158
  end
108
159
 
109
160
 
@@ -111,12 +162,8 @@ module TestaAppiumDriver
111
162
  # @return [TestaAppiumDriver::Locator]
112
163
  def wait_until_exists(timeout = nil)
113
164
  timeout = @driver.get_timeouts["implicit"] / 1000 if timeout.nil?
114
- start_time = Time.now.to_f
115
- until exists?
116
- raise "wait until exists timeout exceeded" if start_time + timeout < Time.now.to_f
117
- sleep EXISTS_WAIT
118
- end
119
- self
165
+ args = {timeout: timeout}
166
+ _wait(:until, args)
120
167
  end
121
168
 
122
169
 
@@ -124,28 +171,31 @@ module TestaAppiumDriver
124
171
  # @return [TestaAppiumDriver::Locator]
125
172
  def wait_while_exists(timeout = nil)
126
173
  timeout = @driver.get_timeouts["implicit"] / 1000 if timeout.nil?
127
- start_time = Time.now.to_f
128
- while exists?
129
- raise "wait until exists timeout exceeded" if start_time + timeout < Time.now.to_f
130
- sleep EXISTS_WAIT
131
- end
132
- self
174
+ args = {timeout: timeout}
175
+ _wait(:while, args)
176
+ end
177
+
178
+
179
+ def wait_while(timeout = nil, args = {})
180
+ args[:timeout] = timeout
181
+ _wait(:while, args)
182
+ end
183
+
184
+ def wait_until(timeout = nil, args = {})
185
+ args[:timeout] = timeout
186
+ _wait(:until, args)
133
187
  end
134
188
 
135
189
 
136
190
  # all timeouts are disabled before check, and enabled after check
137
191
  # @return [boolean] true if it exists in the page regardless if visible or not
138
192
  def exists?
139
- @driver.disable_wait_for_idle
140
- @driver.disable_implicit_wait
141
193
  found = true
142
194
  begin
143
- execute(skip_cache: true)
195
+ execute(skip_cache: true, ignore_implicit_wait: true)
144
196
  rescue StandardError
145
197
  found = false
146
198
  end
147
- @driver.enable_implicit_wait
148
- @driver.enable_wait_for_idle
149
199
  found
150
200
  end
151
201
 
@@ -171,15 +221,23 @@ module TestaAppiumDriver
171
221
 
172
222
  def [](instance)
173
223
  raise "Cannot add index selector to non-Array" if @single
174
- 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
175
225
  locator = self.dup
176
226
  locator.strategy = FIND_STRATEGY_UIAUTOMATOR
177
227
  locator.ui_selector = "#{@ui_selector}.instance(#{instance})"
178
228
  locator.single = true
179
229
  locator.can_use_id_strategy = false
180
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
181
238
  else
182
- from_element = self.execute[instance]
239
+ from_element = self.dup
240
+ from_element.index_for_multiple = instance
183
241
  params = {}.merge({single: true, scrollable_locator: @scrollable_locator})
184
242
  #params[:strategy] = FIND_STRATEGY_XPATH
185
243
  #params[:strategy_reason] = "retrieved instance of a array"
@@ -216,7 +274,8 @@ module TestaAppiumDriver
216
274
  xpath: @xpath_selector,
217
275
  scrollable: @scrollable_locator.nil? ? nil : @scrollable_locator.to_s,
218
276
  scroll_orientation: @scroll_orientation,
219
- resolved: strategy_and_selector
277
+ resolved: strategies_and_selectors,
278
+ index_for_multiple: @index_for_multiple
220
279
  }
221
280
  end
222
281
 
@@ -254,7 +313,15 @@ module TestaAppiumDriver
254
313
  end
255
314
 
256
315
  def click
257
- perform_driver_method(:click)
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
258
325
  end
259
326
 
260
327
  def send_key(*args)
@@ -284,15 +351,20 @@ module TestaAppiumDriver
284
351
  # @return [TestaAppiumDriver::Locator]
285
352
  def children
286
353
  raise "Cannot add children selector to array" unless @single
287
- 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?
288
355
 
289
356
  locator = self.dup
290
- locator.strategy = FIND_STRATEGY_XPATH
291
357
  locator.strategy_reason = "children"
292
358
  locator.xpath_selector += "/*"
293
359
  locator.single = false
294
360
  locator.last_selector_adjacent = true
295
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
296
368
  locator
297
369
  end
298
370
 
@@ -301,14 +373,20 @@ module TestaAppiumDriver
301
373
  # @return [TestaAppiumDriver::Locator]
302
374
  def child
303
375
  raise "Cannot add children selector to array" unless @single
304
- 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?
305
377
 
306
378
  locator = self.dup
307
- locator.strategy = FIND_STRATEGY_XPATH
379
+
308
380
  locator.strategy_reason = "child"
309
381
  locator.xpath_selector += "/*[1]"
310
382
  locator.single = true
311
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
312
390
  locator
313
391
  end
314
392
 
@@ -401,6 +479,51 @@ module TestaAppiumDriver
401
479
 
402
480
  private
403
481
 
482
+ def _wait(type, args)
483
+ interval = EXISTS_WAIT
484
+ interval = args[:interval] unless args[:interval].nil?
485
+
486
+ message = "wait #{type} exists timeout exceeded"
487
+ message = args[:message] unless args[:message].nil?
488
+
489
+ if args[:timeout].nil?
490
+ timeout = @driver.get_timeouts["implicit"] / 1000
491
+ else
492
+ timeout = args[:timeout]
493
+ end
494
+
495
+ args.delete(:message)
496
+ args.delete(:interval)
497
+ args.delete(:timeout)
498
+
499
+
500
+
501
+ start_time = Time.now.to_f
502
+ if type == :while
503
+ while exists? && _attributes_match(args)
504
+ raise message if start_time + timeout < Time.now.to_f
505
+ sleep interval
506
+ end
507
+ else
508
+ until exists? && _attributes_match(args)
509
+ raise message if start_time + timeout < Time.now.to_f
510
+ sleep interval
511
+ end
512
+ end
513
+ self
514
+ end
515
+
516
+ def _attributes_match(attributes)
517
+ all_match = true
518
+ attributes.each do |key, value|
519
+ unless attribute(key) == value
520
+ all_match = false
521
+ break
522
+ end
523
+ end
524
+ all_match
525
+ end
526
+
404
527
  #noinspection RubyNilAnalysis
405
528
  def perform_driver_method(name, *args)
406
529
  elements = execute
@@ -415,6 +538,42 @@ module TestaAppiumDriver
415
538
  locator.single = false unless single # switching from single result to multiple
416
539
  locator.xpath_selector += hash_to_xpath(@driver.device, selectors, single)
417
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
418
577
  end
419
578
 
420
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
- ensure
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 drag_to(x, y)
262
- x0 = @bounds.center.x
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
- !@deadzone.nil? ||
34
- !@scrollable.from_element.instance_of?(TestaAppiumDriver::Driver) # uiautomator cannot resolve nested scrollable
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(x, y)
202
- w3c_drag_to(x, y)
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?