testa_appium_driver 0.1.21 → 0.1.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +23 -0
  3. data/.gitignore +17 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +6 -0
  6. data/Gemfile.lock +1 -1
  7. data/bin/console +17 -0
  8. data/bin/setup +8 -0
  9. data/lib/testa_appium_driver/android/class_selectors.rb +438 -0
  10. data/lib/testa_appium_driver/android/driver.rb +71 -0
  11. data/lib/testa_appium_driver/android/locator/attributes.rb +115 -0
  12. data/lib/testa_appium_driver/android/locator.rb +142 -0
  13. data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +62 -0
  14. data/lib/testa_appium_driver/android/selenium_element.rb +12 -0
  15. data/lib/testa_appium_driver/common/bounds.rb +150 -0
  16. data/lib/testa_appium_driver/common/constants.rb +38 -0
  17. data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +12 -0
  18. data/lib/testa_appium_driver/common/helpers.rb +272 -0
  19. data/lib/testa_appium_driver/common/locator/scroll_actions.rb +390 -0
  20. data/lib/testa_appium_driver/common/locator.rb +640 -0
  21. data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +4 -0
  22. data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +380 -0
  23. data/lib/testa_appium_driver/common/scroll_actions.rb +275 -0
  24. data/lib/testa_appium_driver/common/selenium_element.rb +19 -0
  25. data/lib/testa_appium_driver/driver.rb +338 -0
  26. data/lib/testa_appium_driver/ios/driver.rb +49 -0
  27. data/lib/testa_appium_driver/ios/locator/attributes.rb +89 -0
  28. data/lib/testa_appium_driver/ios/locator.rb +73 -0
  29. data/lib/testa_appium_driver/ios/selenium_element.rb +8 -0
  30. data/lib/testa_appium_driver/ios/type_selectors.rb +201 -0
  31. data/lib/testa_appium_driver.rb +4 -0
  32. data/testa_appium_driver.gemspec +6 -2
  33. metadata +39 -11
  34. data/appium-driver.iml +0 -79
@@ -0,0 +1,640 @@
1
+ require_relative 'locator/scroll_actions'
2
+
3
+ module ::TestaAppiumDriver
4
+ # noinspection RubyTooManyInstanceVariablesInspection,RubyTooManyMethodsInspection
5
+ class Locator
6
+ include Helpers
7
+
8
+ attr_accessor :xpath_selector, :single, :driver, :strategy, :strategy_reason, :can_use_id_strategy, :image_selector,
9
+ :from_element, :scroll_orientation, :scroll_deadzone, :scrollable_locator, :default_find_strategy,
10
+ :default_scroll_strategy, :index_for_multiple
11
+
12
+ # @type [Boolean] used to determine if last selector was one of siblings or children. Only in those selectors we can reliably use xpath array [instance] selector
13
+ attr_accessor :last_selector_adjacent
14
+
15
+ # locator parameters are:
16
+ # single: true or false
17
+ # scrollable_locator: [TestaAppiumDriver::Locator, nil] for scrolling if needed later
18
+ # default_find_strategy: default strategy if find element strategy is not enforced
19
+ # default_scroll_strategy: default strategy for scrolling if not enforced
20
+ #
21
+ # @param [TestaAppiumDriver::Driver] driver
22
+ # @param [TestaAppiumDriver::Driver, TestaAppiumDriver::Locator, Selenium::WebDriver::Element] from_element from which element to execute the find_element
23
+ # @param [Hash] params selectors and params for locator
24
+ def initialize(driver, from_element, params = {})
25
+ # @type [TestaAppiumDriver::Driver]
26
+ @driver = driver
27
+ @index_for_multiple = nil
28
+ @image_selector = nil
29
+
30
+ params, selectors = extract_selectors_from_params(params)
31
+ single = params[:single]
32
+
33
+ @single = single
34
+
35
+ if selectors[:image].nil?
36
+ if from_element.instance_of?(TestaAppiumDriver::Locator) && !from_element.image_selector.nil?
37
+ raise "Cannot chain non-image selectors to image selectors"
38
+ end
39
+ else
40
+ handle_image_selector(selectors, params)
41
+ end
42
+
43
+ selectors[:id] = selectors[:name] unless selectors[:name].nil?
44
+ if from_element.instance_of?(::Selenium::WebDriver::Element) ||
45
+ from_element.instance_of?(::Appium::Core::Element) ||
46
+ from_element.instance_of?(::TestaAppiumDriver::Locator)
47
+ @xpath_selector = "."
48
+ @xpath_selector += hash_to_xpath(@driver.device, selectors, single)
49
+ else
50
+ @xpath_selector = hash_to_xpath(@driver.device, selectors, single)
51
+ end
52
+
53
+ @from_element = from_element
54
+ @default_find_strategy = params[:default_find_strategy]
55
+ @default_scroll_strategy = params[:default_scroll_strategy]
56
+
57
+ @can_use_id_strategy = is_only_id_selector?(selectors)
58
+ if @can_use_id_strategy
59
+ if @driver.device == :android
60
+ @can_use_id_strategy = resolve_id(selectors[:id])
61
+ else
62
+ @can_use_id_strategy = selectors[:id]
63
+ end
64
+ end
65
+
66
+ @strategy = params[:strategy]
67
+ @strategy_reason = params[:strategy_reason]
68
+
69
+ @last_selector_adjacent = false
70
+
71
+ init(params, selectors, single)
72
+ end
73
+
74
+ def is_only_id_selector?(selectors)
75
+ # since, name and id is the same thing for iOS,
76
+ if @driver.device == :android
77
+ selectors.keys.count == 1 && !selectors[:id].nil? && !selectors[:id].is_a?(Regexp)
78
+ else
79
+ selector = nil
80
+ if selectors.keys.count == 2
81
+ if !selectors[:id].nil? && !selectors[:id].nil? && selectors[:id] == selectors[:name]
82
+ selector = selectors[:id]
83
+ else
84
+ selector = nil
85
+ end
86
+ elsif selectors.keys.count == 1
87
+ selector = selectors[:id] unless selectors[:id].nil?
88
+ selector = selectors[:name] unless selectors[:name].nil?
89
+ end
90
+
91
+ return false if selector.nil?
92
+
93
+ !selector.is_a?(Regexp)
94
+ end
95
+ end
96
+
97
+ # method missing is used to fetch the element before executing additional commands like click, send_key, count
98
+ def method_missing(method, *args, &block)
99
+ r = execute.send(method, *args, &block)
100
+ @driver.invalidate_cache
101
+ r
102
+ end
103
+
104
+ # @param [Boolean] skip_cache if true it will skip cache check and store
105
+ # @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
106
+ # @return [Selenium::WebDriver::Element, Array]
107
+ def execute(skip_cache: false, force_cache_element: nil, ignore_implicit_wait: false)
108
+ return force_cache_element unless force_cache_element.nil?
109
+
110
+ # if we are looking for current element, then return from_element
111
+ # for example when we have driver.element.elements[1].click
112
+ # elements[2] will be resolved with xpath because we are looking for multiple elements from element
113
+ # and since we are looking for instance 2, [](instance) method will return new "empty locator"
114
+ # we are executing click on that "empty locator" so we have to return the instance 2 of elements for the click
115
+ if @xpath_selector == ".//*[1]" && !@from_element.nil? && @image_selector.nil?
116
+ if @from_element.instance_of?(::Selenium::WebDriver::Element) ||
117
+ @from_element.instance_of?(::Appium::Core::Element)
118
+ return @from_element
119
+ end
120
+
121
+ if @from_element.instance_of?(TestaAppiumDriver::Locator)
122
+ return @from_element.execute(
123
+ skip_cache: skip_cache,
124
+ force_cache_element: force_cache_element,
125
+ ignore_implicit_wait: ignore_implicit_wait
126
+ )
127
+ end
128
+
129
+ return @from_element
130
+ end
131
+
132
+ r = @driver.execute(@from_element, @single, strategies_and_selectors,
133
+ skip_cache: skip_cache,
134
+ ignore_implicit_wait: ignore_implicit_wait)
135
+ r = r[@index_for_multiple] if !@index_for_multiple.nil? && !@single
136
+ r
137
+ end
138
+
139
+ def when_exists(timeout = nil, &block)
140
+ found = false
141
+ begin
142
+ wait_until_exists(timeout)
143
+ found = true
144
+ rescue StandardError
145
+ # ignored
146
+ end
147
+ if found
148
+ if block_given? # block is given
149
+ block.call(self) # use call to execute the block
150
+ else
151
+ # the value of block_argument becomes nil if you didn't give a block
152
+ # block was not given
153
+ end
154
+ end
155
+ self
156
+ end
157
+
158
+ # @param [Integer] timeout in seconds
159
+ # @return [TestaAppiumDriver::Locator]
160
+ def wait_until_exists(timeout = nil)
161
+ args = { timeout: timeout }
162
+ _wait(:until, args)
163
+ end
164
+
165
+ # @param [Integer] timeout in seconds
166
+ # @return [TestaAppiumDriver::Locator]
167
+ def wait_while_exists(timeout = nil)
168
+ args = { timeout: timeout }
169
+ _wait(:while, args)
170
+ end
171
+
172
+ def wait_while(timeout = nil, args = {})
173
+ args[:timeout] = timeout
174
+ _wait(:while, args)
175
+ end
176
+
177
+ def wait_until(timeout = nil, args = {})
178
+ args[:timeout] = timeout
179
+ _wait(:until, args)
180
+ end
181
+
182
+ # all timeouts are disabled before check, and enabled after check
183
+ # @return [boolean]
184
+ def exists?
185
+ # TODO: should be called visible? or present?
186
+ found = true
187
+ begin
188
+ r = execute(skip_cache: true, ignore_implicit_wait: true)
189
+ return r.count.positive? if r.is_a?(Array)
190
+ return r.displayed? if @driver.ios?
191
+ rescue StandardError
192
+ found = false
193
+ end
194
+ found
195
+ end
196
+
197
+ def in_viewport?
198
+ ws = @driver.window_size
199
+ viewport_height = ws.height
200
+ viewport_width = ws.width
201
+
202
+ check = proc do |b|
203
+ !(b.bottom_right.y.negative? || b.top_left.y > viewport_height ||
204
+ b.top_left.x.negative? || b.bottom_right.x > viewport_width)
205
+ end
206
+
207
+ begin
208
+ r = execute(skip_cache: true, ignore_implicit_wait: true)
209
+ r = [r] unless r.is_a?(Array)
210
+ return false if r.count.zero?
211
+
212
+ r.all? { |e| check.call(e.bounds) }
213
+ rescue StandardError
214
+ return false
215
+ end
216
+ end
217
+
218
+ # @return [TestaAppiumDriver::Locator]
219
+ def first
220
+ self[0]
221
+ end
222
+
223
+ # @return [TestaAppiumDriver::Locator]
224
+ def second
225
+ self[1]
226
+ end
227
+
228
+ # @return [TestaAppiumDriver::Locator]
229
+ def third
230
+ self[2]
231
+ end
232
+
233
+ # @return [TestaAppiumDriver::Locator]
234
+ def last
235
+ self[-1]
236
+ end
237
+
238
+ def [](instance)
239
+ raise "Cannot add index selector to non-Array" if @single
240
+
241
+ if ((@strategy.nil? && !@last_selector_adjacent && @driver.device == :android) || @strategy == FIND_STRATEGY_UIAUTOMATOR) && instance >= 0
242
+ locator = self.dup
243
+ locator.strategy = FIND_STRATEGY_UIAUTOMATOR
244
+ locator.ui_selector = "#{@ui_selector}.instance(#{instance})"
245
+ locator.single = true
246
+ locator.can_use_id_strategy = false
247
+ locator
248
+ elsif (@driver.device == :ios && !@last_selector_adjacent && @strategy.nil?) || @strategy == FIND_STRATEGY_CLASS_CHAIN
249
+ locator = self.dup
250
+ locator.strategy = FIND_STRATEGY_CLASS_CHAIN
251
+ locator.class_chain_selector += "[#{instance + 1}]"
252
+ locator.single = true
253
+ locator.can_use_id_strategy = false
254
+ locator
255
+ else
256
+ from_element = self.dup
257
+ from_element.index_for_multiple = instance
258
+ params = {}.merge({ single: true, scrollable_locator: @scrollable_locator })
259
+ # params[:strategy] = FIND_STRATEGY_XPATH
260
+ # params[:strategy_reason] = "retrieved instance of a array"
261
+ params[:default_find_strategy] = @default_find_strategy
262
+ params[:default_scroll_strategy] = @default_scroll_strategy
263
+ Locator.new(@driver, from_element, params)
264
+ end
265
+ end
266
+
267
+ # @param [TestaAppiumDriver::Locator, Selenium::WebDriver::Element, Array] other
268
+ # noinspection RubyNilAnalysis,RubyUnnecessaryReturnStatement
269
+ def ==(other)
270
+ elements = execute
271
+ other = other.execute if other.is_a?(TestaAppiumDriver::Locator)
272
+
273
+ if elements.is_a?(Array)
274
+ return false unless other.is_a?(Array)
275
+ return false if other.count != elements.count
276
+
277
+ (elements - other).empty?
278
+ else
279
+ return false if other.is_a?(Array)
280
+
281
+ elements == other
282
+ end
283
+ end
284
+
285
+ def as_json
286
+ {
287
+ strategy: @strategy,
288
+ default_strategy: @default_find_strategy,
289
+ single: @single,
290
+ uiautomator: defined?(self.ui_selector) ? ui_selector : nil,
291
+ xpath: @xpath_selector,
292
+ scroll_orientation: @scroll_orientation,
293
+ resolved: strategies_and_selectors,
294
+ index_for_multiple: @index_for_multiple,
295
+ }
296
+ end
297
+
298
+ def to_s
299
+ JSON.dump(as_json)
300
+ end
301
+
302
+ def to_ary
303
+ [self.to_s]
304
+ end
305
+
306
+ # @return [TestaAppiumDriver::Locator]
307
+ def as_scrollable(orientation: :vertical, top: nil, bottom: nil, right: nil, left: nil)
308
+ @scroll_orientation = orientation
309
+ if !top.nil? || !bottom.nil? || !right.nil? || !left.nil?
310
+ @scroll_deadzone = {}
311
+ @scroll_deadzone[:top] = top.to_f unless top.nil?
312
+ @scroll_deadzone[:bottom] = bottom.to_f unless bottom.nil?
313
+ @scroll_deadzone[:right] = right.to_f unless right.nil?
314
+ @scroll_deadzone[:left] = left.to_f unless left.nil?
315
+ end
316
+ @scrollable_locator = self.dup
317
+ self
318
+ end
319
+
320
+ def first_and_last_leaf
321
+ @driver.first_and_last_leaf(execute)
322
+ end
323
+
324
+ def first_and_last_child
325
+ @driver.first_and_last_child(execute)
326
+ end
327
+
328
+ def double_click(x = nil, y = nil)
329
+ click(x, y, double: true)
330
+ end
331
+
332
+ # if both x or y, or both are not given, will click in the center of the element
333
+ # @param x If positive integer, will offset the click from the left side, if negative integer, will offset the click from the right. If float value is given, it will threat it as percentage offset, giving it 0.5 will click in the middle
334
+ # @param y If positive integer, will offset the click from the bottom side, if negative integer, will offset the click from the top. If float value is given, it will threat it as percentage offset, giving it 0.5 will click in the middle
335
+ def click(x = nil, y = nil, double: false, by_coords: false)
336
+ if !x.nil? && !y.nil? || double || by_coords
337
+
338
+ x = 0.5 if x.nil?
339
+ y = 0.5 if y.nil?
340
+
341
+ b = self.bounds
342
+ if x.is_a?(Integer)
343
+ if x >= 0
344
+ x = b.top_left.x + x
345
+ else
346
+ x = b.bottom_right.x + x
347
+ end
348
+ elsif x.is_a?(Float) && x <= 1.0 && x >= 0
349
+ x = b.top_left.x + b.width * x
350
+ else
351
+ raise "x value #{x} not supported. Use integer as pixel or float (0..1) as percentage of element width"
352
+ end
353
+
354
+ if y.is_a?(Integer)
355
+ if y >= 0
356
+ y = b.top_left.y + y
357
+ else
358
+ y = b.bottom_right + y
359
+ end
360
+ elsif y.is_a?(Float) && y <= 1.0 && y >= 0
361
+ y = b.top_left.y + b.height * y
362
+ else
363
+ raise "y value #{x} not supported. Use integer as pixel or float (0..1) as percentage of element height"
364
+ end
365
+
366
+ action_builder = @driver.action
367
+ f1 = action_builder.add_pointer_input(:touch, "finger1")
368
+ f1.create_pointer_move(duration: 0.1, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
369
+ f1.create_pointer_down(:left)
370
+ f1.create_pointer_up(:left)
371
+ if double
372
+ if @driver.ios?
373
+ # NOTE: ios is stupid, we have to do another move
374
+ # NOTE: ios is stupid, works very wierd if duration is 0
375
+ f1.create_pointer_move(duration: 0.1, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
376
+ end
377
+ f1.create_pause(0.1)
378
+ f1.create_pointer_down(:left)
379
+ f1.create_pointer_up(:left)
380
+ end
381
+ @driver.perform_actions [f1]
382
+ else
383
+ perform_driver_method(:click)
384
+ end
385
+ end
386
+
387
+ alias_method :tap, :click
388
+ alias_method :double_tap, :double_click
389
+
390
+ def send_key(*args)
391
+ perform_driver_method(:send_keys, *args)
392
+ end
393
+
394
+ def clear
395
+ perform_driver_method(:clear)
396
+ end
397
+
398
+ # Return parent element
399
+ # @return [TestaAppiumDriver::Locator]
400
+ def parent
401
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "parent") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
402
+ raise "Cannot add parent selector to a retrieved instance of a class array" if (@xpath_selector == ".//*" || @xpath_selector == ".//*[1]") && !@from_element.nil?
403
+
404
+ locator = self.dup
405
+ locator.strategy = FIND_STRATEGY_XPATH
406
+ locator.strategy_reason = "parent"
407
+ locator.xpath_selector += "/.."
408
+ locator.can_use_id_strategy = false
409
+ locator
410
+ end
411
+
412
+ # Return all children elements
413
+ # @return [TestaAppiumDriver::Locator]
414
+ def children
415
+ raise "Cannot add children selector to array" unless @single
416
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "children") if @strategy != FIND_STRATEGY_XPATH && @strategy != FIND_STRATEGY_CLASS_CHAIN && !@strategy.nil?
417
+
418
+ locator = self.dup
419
+ locator.strategy_reason = "children"
420
+ locator.xpath_selector += "/*"
421
+ locator.single = false
422
+ locator.last_selector_adjacent = true
423
+ locator.can_use_id_strategy = false
424
+
425
+ if @driver.device == :android
426
+ locator.strategy = FIND_STRATEGY_XPATH
427
+ else
428
+ locator.class_chain_selector += "/*"
429
+ end
430
+ locator
431
+ end
432
+
433
+ # Return first child element
434
+ # @return [TestaAppiumDriver::Locator]
435
+ def child
436
+ raise "Cannot add children selector to array" unless @single
437
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "child") if @strategy != FIND_STRATEGY_XPATH && @strategy != FIND_STRATEGY_CLASS_CHAIN && !@strategy.nil?
438
+
439
+ locator = self.dup
440
+
441
+ locator.strategy_reason = "child"
442
+ locator.xpath_selector += "/*[1]"
443
+ locator.single = true
444
+ locator.can_use_id_strategy = false
445
+
446
+ if @driver.device == :android
447
+ locator.strategy = FIND_STRATEGY_XPATH
448
+ else
449
+ locator.class_chain_selector += "/*[1]"
450
+ end
451
+ locator
452
+ end
453
+
454
+ # @return [TestaAppiumDriver::Locator]
455
+ def siblings
456
+ raise "Cannot add siblings selector to array" unless @single
457
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "siblings") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
458
+ raise "Cannot add siblings selector to a retrieved instance of a class array" if (@xpath_selector == ".//*" || @xpath_selector == ".//*[1]") && !@from_element.nil?
459
+
460
+ locator = self.dup
461
+ locator.strategy = FIND_STRATEGY_XPATH
462
+ locator.strategy_reason = "siblings"
463
+ locator.xpath_selector += "/../*[not(@index=\"#{index}\")]"
464
+ locator.single = false
465
+ locator.last_selector_adjacent = true
466
+ locator.can_use_id_strategy = false
467
+ locator
468
+ end
469
+
470
+ # @return [TestaAppiumDriver::Locator]
471
+ def preceding_siblings
472
+ raise "Cannot add preceding_siblings selector to array" unless @single
473
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "preceding_siblings") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
474
+ raise "Cannot add preceding_siblings selector to a retrieved instance of a class array" if (@xpath_selector == ".//*" || @xpath_selector == ".//*[1]") && !@from_element.nil?
475
+
476
+ locator = self.dup
477
+ locator.strategy = FIND_STRATEGY_XPATH
478
+ locator.strategy_reason = "preceding_siblings"
479
+ locator.xpath_selector += "/../*[position() < #{index + 1}]" # position() starts from 1
480
+ locator.single = false
481
+ locator.last_selector_adjacent = true
482
+ locator.can_use_id_strategy = false
483
+ locator
484
+ end
485
+
486
+ # @return [TestaAppiumDriver::Locator]
487
+ def preceding_sibling
488
+ raise "Cannot add preceding_sibling selector to array" unless @single
489
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "preceding_sibling") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
490
+ raise "Cannot add preceding siblings selector to a retrieved instance of a class array" if (@xpath_selector == ".//*" || @xpath_selector == ".//*[1]") && !@from_element.nil?
491
+
492
+ locator = self.dup
493
+ locator.strategy = FIND_STRATEGY_XPATH
494
+ locator.strategy_reason = "preceding_sibling"
495
+ i = index
496
+ locator.single = true
497
+ return nil if i.zero?
498
+
499
+ locator.xpath_selector += "/../*[@index=\"#{i - 1}\"]"
500
+ locator.last_selector_adjacent = true
501
+ locator.can_use_id_strategy = false
502
+ locator
503
+ end
504
+
505
+ # @return [TestaAppiumDriver::Locator]
506
+ def following_siblings
507
+ raise "Cannot add following_siblings selector to array" unless @single
508
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "following_siblings") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
509
+ raise "Cannot add following_siblings selector to a retrieved instance of a class array" if (@xpath_selector == ".//*" || @xpath_selector == ".//*[1]") && !@from_element.nil?
510
+
511
+ locator = self.dup
512
+ locator.strategy = FIND_STRATEGY_XPATH
513
+ locator.strategy_reason = "following_siblings"
514
+ i = index.to_i
515
+ locator.xpath_selector += "/../*[position() > #{i + 1}]" # position() starts from 1
516
+ locator.single = false
517
+ locator.last_selector_adjacent = true
518
+ locator.can_use_id_strategy = false
519
+ locator
520
+ end
521
+
522
+ # @return [TestaAppiumDriver::Locator]
523
+ def following_sibling
524
+ raise "Cannot add following_sibling selector to array" unless @single
525
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_XPATH, "following_sibling") if @strategy != FIND_STRATEGY_XPATH && !@strategy.nil?
526
+ raise "Cannot add following_sibling selector to a retrieved instance of a class array" if (@xpath_selector == ".//*" || @xpath_selector == ".//*[1]") && !@from_element.nil?
527
+ locator = self.dup
528
+ locator.strategy = FIND_STRATEGY_XPATH
529
+ locator.strategy_reason = "following_sibling"
530
+ i = index.to_i
531
+ locator.single = true
532
+
533
+ # in ios, the index can be false (0 when casted to_i), but inspector shows index as 0
534
+ # return nil if i.zero?
535
+
536
+ locator.xpath_selector += "/../*[@index=\"#{i + 1}\"]"
537
+ locator.last_selector_adjacent = true
538
+ locator.can_use_id_strategy = false
539
+ locator
540
+ end
541
+
542
+ private
543
+
544
+ def _wait(type, args)
545
+ interval = EXISTS_WAIT
546
+ interval = args[:interval] unless args[:interval].nil?
547
+
548
+ message = "wait #{type} exists timeout exceeded"
549
+ message = args[:message] unless args[:message].nil?
550
+
551
+ if args[:timeout].nil?
552
+ # timeout = @driver.get_timeouts["implicit"] / 1000
553
+ timeout = 10
554
+ else
555
+ timeout = args[:timeout]
556
+ end
557
+
558
+ args.delete(:message)
559
+ args.delete(:interval)
560
+ args.delete(:timeout)
561
+
562
+ start_time = Time.now.to_f
563
+ if type == :while
564
+ while exists? && _attributes_match(args)
565
+ raise message if start_time + timeout < Time.now.to_f
566
+
567
+ sleep interval
568
+ end
569
+ else
570
+ until exists? && _attributes_match(args)
571
+ raise message if start_time + timeout < Time.now.to_f
572
+
573
+ sleep interval
574
+ end
575
+ end
576
+ self
577
+ end
578
+
579
+ def _attributes_match(attributes)
580
+ all_match = true
581
+ attributes.each do |key, value|
582
+ unless testa_attribute(key) == value
583
+ all_match = false
584
+ break
585
+ end
586
+ end
587
+ all_match
588
+ end
589
+
590
+ # noinspection RubyNilAnalysis
591
+ def perform_driver_method(name, *args)
592
+ elements = execute
593
+ if elements.is_a?(Array)
594
+ elements.map { |e| e.send(name, *args) }
595
+ else
596
+ elements.send(name, *args)
597
+ end
598
+ end
599
+
600
+ def add_xpath_child_selectors(locator, selectors, single)
601
+ locator.single = false unless single # switching from single result to multiple
602
+ locator.xpath_selector += hash_to_xpath(@driver.device, selectors, single)
603
+ end
604
+
605
+ def handle_image_selector(selectors, params)
606
+ image_match_threshold = 0.4
607
+ image_match_threshold = params[:imageMatchThreshold] unless params[:imageMatchThreshold].nil?
608
+ image_match_threshold = params[:threshold] unless params[:threshold].nil?
609
+ fix_image_find_screenshot_dims = true
610
+ fix_image_find_screenshot_dims = params[:fixImageFindScreenshotDims] unless params[:fixImageFindScreenshotDims].nil?
611
+ fix_image_template_size = false
612
+ fix_image_template_size = params[:fixImageTemplateSize] unless params[:fixImageTemplateSize].nil?
613
+ fix_image_template_scale = false
614
+ fix_image_template_scale = params[:fixImageTemplateScale] unless params[:fixImageTemplateScale].nil?
615
+ default_image_template_scale = 1.0
616
+ default_image_template_scale = params[:defaultImageTemplateScale] unless params[:defaultImageTemplateScale].nil?
617
+ check_for_image_element_staleness = true
618
+ check_for_image_element_staleness = params[:checkForImageElementStaleness] unless params[:checkForImageElementStaleness].nil?
619
+ auto_update_image_element_position = false
620
+ auto_update_image_element_position = params[:autoUpdateImageElementPosition] unless params[:autoUpdateImageElementPosition].nil?
621
+ image_element_tap_strategy = "w3cActions"
622
+ image_element_tap_strategy = params[:imageElementTapStrategy] unless params[:imageElementTapStrategy].nil?
623
+ get_matched_image_result = false
624
+ get_matched_image_result = params[:getMatchedImageResult] unless params[:getMatchedImageResult].nil?
625
+
626
+ @image_selector = {
627
+ image: selectors[:image],
628
+ imageMatchThreshold: image_match_threshold,
629
+ fixImageFindScreenshotDims: fix_image_find_screenshot_dims,
630
+ fixImageTemplateSize: fix_image_template_size,
631
+ fixImageTemplateScale: fix_image_template_scale,
632
+ defaultImageTemplateScale: default_image_template_scale,
633
+ checkForImageElementStaleness: check_for_image_element_staleness,
634
+ autoUpdateImageElementPosition: auto_update_image_element_position,
635
+ imageElementTapStrategy: image_element_tap_strategy,
636
+ getMatchedImageResult: get_matched_image_result,
637
+ }
638
+ end
639
+ end
640
+ end
@@ -0,0 +1,4 @@
1
+ module TestaAppiumDriver
2
+ module JsonWireScrollActions
3
+ end
4
+ end