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