testa_appium_driver 0.1.3 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -2
  3. data/.idea/deployment.xml +10 -2
  4. data/.idea/inspectionProfiles/Project_Default.xml +8 -8
  5. data/.idea/runConfigurations/Android_Test.xml +41 -41
  6. data/.idea/runConfigurations.xml +10 -0
  7. data/.idea/sshConfigs.xml +3 -0
  8. data/.idea/webServers.xml +7 -0
  9. data/.rspec +3 -3
  10. data/.rubocop.yml +13 -13
  11. data/CHANGELOG.md +5 -5
  12. data/CODE_OF_CONDUCT.md +102 -102
  13. data/Gemfile +12 -12
  14. data/LICENSE.txt +21 -21
  15. data/README.md +18 -5
  16. data/Rakefile +12 -12
  17. data/bin/console +17 -17
  18. data/bin/setup +8 -8
  19. data/lib/testa_appium_driver/android/class_selectors.rb +101 -16
  20. data/lib/testa_appium_driver/android/driver.rb +19 -1
  21. data/lib/testa_appium_driver/android/locator.rb +22 -7
  22. data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +0 -14
  23. data/lib/testa_appium_driver/common/constants.rb +1 -0
  24. data/lib/testa_appium_driver/common/helpers.rb +5 -1
  25. data/lib/testa_appium_driver/common/locator/scroll_actions.rb +152 -58
  26. data/lib/testa_appium_driver/common/locator.rb +160 -25
  27. data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +14 -36
  28. data/lib/testa_appium_driver/common/scroll_actions.rb +30 -8
  29. data/lib/testa_appium_driver/common/selenium_element.rb +2 -2
  30. data/lib/testa_appium_driver/driver.rb +99 -29
  31. data/lib/testa_appium_driver/ios/driver.rb +14 -0
  32. data/lib/testa_appium_driver/ios/locator.rb +10 -5
  33. data/lib/testa_appium_driver/ios/type_selectors.rb +0 -13
  34. data/lib/testa_appium_driver/version.rb +5 -5
  35. data/testa_appium_driver.gemspec +1 -1
  36. data/testa_appium_driver.iml +39 -5
  37. metadata +7 -6
@@ -2,6 +2,7 @@ module TestaAppiumDriver
2
2
  #noinspection RubyTooManyMethodsInspection
3
3
  class Locator
4
4
 
5
+ # performs a long tap on the retrieved element
5
6
  # @param [Float] duration in seconds
6
7
  def long_tap(duration = LONG_TAP_DURATION)
7
8
  action_builder = @driver.action
@@ -15,173 +16,254 @@ module TestaAppiumDriver
15
16
  @driver.perform_actions [f1]
16
17
  end
17
18
 
18
- # @return [Array] array of [Selenium::WebDriver::Element]
19
- def each(deadzone: nil, skip_scroll_to_start: false, &block)
19
+
20
+ # scrolls to the start of the scrollable containers and scrolls to the end,
21
+ # everytime a locator element is found the given block is executed
22
+ # @return [Array<Selenium::WebDriver::Element>]
23
+ def each(top: nil, bottom: nil, right: nil, left: nil, direction: nil, &block)
24
+ deadzone = _process_deadzone(top, bottom, right, left)
20
25
  raise "Each can only be performed on multiple elements locator" if @single
21
26
  deadzone = @scrollable_locator.scroll_deadzone if deadzone.nil? && !@scrollable_locator.nil?
22
27
  sa = ScrollActions.new(@scrollable_locator,
23
28
  locator: self,
24
29
  deadzone: deadzone,
25
30
  default_scroll_strategy: @default_scroll_strategy)
26
- sa.each(skip_scroll_to_start, &block)
31
+ if direction.nil?
32
+ sa.each(&block)
33
+ else
34
+ sa.send("each_#{direction}", &block)
35
+ end
36
+ end
37
+
38
+ # scrolls down from the current page view (without prior scrolling to the top) and
39
+ # everytime a locator element is found the given block is executed
40
+ # @return [Array<Selenium::WebDriver::Element>]
41
+ def each_down(top: nil, bottom: nil, right: nil, left: nil, &block)
42
+ each(top: top, bottom: bottom, right: right, left: left, direction: :down, &block)
43
+ end
44
+
45
+ # scrolls up from the current page view (without prior scrolling to the bottom) and
46
+ # everytime a locator element is found the given block is executed
47
+ # @return [Array<Selenium::WebDriver::Element>]
48
+ def each_up(top: nil, bottom: nil, right: nil, left: nil, &block)
49
+ each(top: top, bottom: bottom, right: right, left: left, direction: :up, &block)
50
+ end
51
+
52
+ # scrolls right from the current page view (without prior scrolling to the left) and
53
+ # everytime a locator element is found the given block is executed
54
+ # @return [Array<Selenium::WebDriver::Element>]
55
+ def each_right(top: nil, bottom: nil, right: nil, left: nil, &block)
56
+ each(top: top, bottom: bottom, right: right, left: left, direction: :right, &block)
57
+ end
58
+
59
+ # scrolls left from the current page view (without prior scrolling to the right) and
60
+ # everytime a locator element is found the given block is executed
61
+ # @return [Array<Selenium::WebDriver::Element>]
62
+ def each_left(top: nil, bottom: nil, right: nil, left: nil, &block)
63
+ each(top: top, bottom: bottom, right: right, left: left, direction: :left, &block)
27
64
  end
28
65
 
29
66
 
30
67
  # Aligns element (by default) on top of the scrollable container, if the element does not exists it will scroll to find it
68
+ # The element is aligned if the the distance from the top/bottom/right/left of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
69
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
70
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
31
71
  # @return [TestaAppiumDriver::Locator]
32
- def align(with = :top, deadzone: nil, raise: false)
72
+ def align(with = :top, top: nil, bottom: nil, right: nil, left: nil, scroll_to_find: false)
33
73
  deadzone = @scrollable_locator.scroll_deadzone if deadzone.nil? && !@scrollable_locator.nil?
34
74
  sa = ScrollActions.new(@scrollable_locator,
35
75
  locator: self,
36
76
  deadzone: deadzone,
37
- default_scroll_strategy: @default_scroll_strategy,
38
- raise: raise)
39
- sa.align(with)
77
+ default_scroll_strategy: @default_scroll_strategy)
78
+ sa.align(with, scroll_to_find)
40
79
  self
41
80
  end
42
81
 
43
82
  # Aligns element on top of the scrollable container, if the element does not exists it will scroll to find it
83
+ # The element is aligned if the the distance from the top of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
84
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
85
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
44
86
  # @return [TestaAppiumDriver::Locator]
45
- def align_top(deadzone: nil)
46
- align(:top, deadzone: deadzone)
87
+ def align_top(top: nil, bottom: nil, right: nil, left: nil)
88
+ align(:top, top: top, bottom: bottom, right: right, left: left)
47
89
  end
48
90
 
49
91
  # Aligns element on bottom of the scrollable container, if the element does not exists it will scroll to find it
92
+ # The element is aligned if the the distance from the bottom of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
93
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
94
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
50
95
  # @return [TestaAppiumDriver::Locator]
51
- def align_bottom(deadzone: nil)
52
- align(:bottom, deadzone: deadzone)
96
+ def align_bottom(top: nil, bottom: nil, right: nil, left: nil)
97
+ align(:bottom, top: top, bottom: bottom, right: right, left: left)
53
98
  end
54
99
 
55
100
  # Aligns element on left of the scrollable container, if the element does not exists it will scroll to find it
101
+ # The element is aligned if the the distance from the left of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
102
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
103
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
56
104
  # @return [TestaAppiumDriver::Locator]
57
- def align_left(deadzone: nil)
58
- align(:left, deadzone: deadzone)
105
+ def align_left(top: nil, bottom: nil, right: nil, left: nil)
106
+ align(:left, top: top, bottom: bottom, right: right, left: left)
59
107
  end
60
108
 
61
109
  # Aligns element on right of the scrollable container, if the element does not exists it will scroll to find it
110
+ # The element is aligned if the the distance from the right of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
111
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
112
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
62
113
  # @return [TestaAppiumDriver::Locator]
63
- def align_right(deadzone: nil)
64
- align(:right, deadzone: deadzone)
114
+ def align_right(top: nil, bottom: nil, right: nil, left: nil)
115
+ align(:right, top: top, bottom: bottom, right: right, left: left)
65
116
  end
66
117
 
67
118
  # Aligns element (by default) on top of the scrollable container, if the element does not exists it raise an exception
119
+ # The element is aligned if the the distance from the top/bottom/right/left of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
120
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
121
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
68
122
  # @return [TestaAppiumDriver::Locator]
69
- def align!(with = :top, deadzone: nil)
70
- align(with, deadzone: deadzone, raise: true)
123
+ def align!(with = :top, top: nil, bottom: nil, right: nil, left: nil)
124
+ align(with, top: top, bottom: bottom, right: right, left: left, scroll_to_find: true)
71
125
  end
72
126
 
73
127
  # Aligns element on top of the scrollable container, if the element does not exists it raise an exception
128
+ # The element is aligned if the the distance from the top of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
129
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
130
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
74
131
  # @return [TestaAppiumDriver::Locator]
75
- def align_top!(deadzone: nil)
76
- align(:top, deadzone: deadzone, raise: true)
132
+ def align_top!(top: nil, bottom: nil, right: nil, left: nil)
133
+ align(:top, top: top, bottom: bottom, right: right, left: left, scroll_to_find: true)
77
134
  end
78
135
 
79
136
  # Aligns element on bottom of the scrollable container, if the element does not exists it raise an exception
137
+ # The element is aligned if the the distance from the bottom of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
138
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
139
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
80
140
  # @return [TestaAppiumDriver::Locator]
81
- def align_bottom!(deadzone: nil)
82
- align(:bottom, deadzone: deadzone, raise: true)
141
+ def align_bottom!(top: nil, bottom: nil, right: nil, left: nil)
142
+ align(:bottom, top: top, bottom: bottom, right: right, left: left, scroll_to_find: true)
83
143
  end
84
144
 
85
145
  # Aligns element on left of the scrollable container, if the element does not exists it raise an exception
146
+ # The element is aligned if the the distance from the left of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
147
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
148
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
86
149
  # @return [TestaAppiumDriver::Locator]
87
- def align_left!(deadzone: nil)
88
- align(:left, deadzone: deadzone, raise: true)
150
+ def align_left!(top: nil, bottom: nil, right: nil, left: nil)
151
+ align(:left, top: top, bottom: bottom, right: right, left: left, scroll_to_find: true)
89
152
  end
90
153
 
91
154
  # Aligns element on right of the scrollable container, if the element does not exists it raise an exception
155
+ # The element is aligned if the the distance from the right of the scrollable container is less than [TestaAppiumDriver::SCROLL_ALIGNMENT_THRESHOLD]
156
+ # If the distance is greater than the threshold, it will attempt to realign it up to 2 more times.
157
+ # The retry mechanism allows alignment even for dynamic layouts when elements are hidden/show when scrolling to certain direction
92
158
  # @return [TestaAppiumDriver::Locator]
93
- def align_right!(deadzone: nil)
94
- align(:right, deadzone: deadzone, raise: true)
159
+ def align_right!(top: nil, bottom: nil, right: nil, left: nil)
160
+ align(:right, top: top, bottom: bottom, right: right, left: left, scroll_to_find: true)
95
161
  end
96
162
 
97
163
 
98
164
  # First scrolls to the beginning of the scrollable container and then scrolls down until element is found or end is reached
99
165
  # @return [TestaAppiumDriver::Locator]
100
- def scroll_to(deadzone: nil, max_scrolls: nil, direction: nil)
166
+ def scroll_to(top: nil, bottom: nil, right: nil, left: nil, max_scrolls: nil, direction: nil)
101
167
  if direction
102
- _scroll_dir_to(deadzone, max_scrolls, direction)
168
+ _scroll_dir_to(_process_deadzone(top, bottom, right, left), max_scrolls, direction)
103
169
  else
104
- _scroll_to(deadzone, max_scrolls)
170
+ _scroll_to(_process_deadzone(top, bottom, right, left), max_scrolls)
105
171
  end
106
172
  end
107
173
 
108
174
 
109
175
  # Scrolls down until element is found or end is reached
110
176
  # @return [TestaAppiumDriver::Locator]
111
- def scroll_down_to(deadzone: nil, max_scrolls: nil)
112
- _scroll_dir_to(deadzone, max_scrolls, :down)
177
+ def scroll_down_to(top: nil, bottom: nil, right: nil, left: nil, max_scrolls: nil)
178
+ _scroll_dir_to(_process_deadzone(top, bottom, right, left), max_scrolls, :down)
113
179
  end
114
180
 
115
181
  # Scrolls up until element is found or end is reached
116
182
  # @return [TestaAppiumDriver::Locator]
117
- def scroll_up_to(deadzone: nil, max_scrolls: nil)
118
- _scroll_dir_to(deadzone, max_scrolls, :up)
183
+ def scroll_up_to(top: nil, bottom: nil, right: nil, left: nil, max_scrolls: nil)
184
+ _scroll_dir_to(_process_deadzone(top, bottom, right, left), max_scrolls, :up)
119
185
  end
120
186
 
121
187
  # Scrolls right until element is found or end is reached
122
188
  # @return [TestaAppiumDriver::Locator]
123
- def scroll_right_to(deadzone: nil, max_scrolls: nil)
124
- _scroll_dir_to(deadzone, max_scrolls, :right)
189
+ def scroll_right_to(top: nil, bottom: nil, right: nil, left: nil, max_scrolls: nil)
190
+ _scroll_dir_to(_process_deadzone(top, bottom, right, left), max_scrolls, :right)
125
191
  end
126
192
 
127
193
 
128
194
  # Scrolls left until element is found or end is reached
129
195
  # @return [TestaAppiumDriver::Locator]
130
- def scroll_left_to(deadzone: nil, max_scrolls: nil)
131
- _scroll_dir_to(deadzone, max_scrolls, :left)
196
+ def scroll_left_to(top: nil, bottom: nil, right: nil, left: nil, max_scrolls: nil)
197
+ _scroll_dir_to(_process_deadzone(top, bottom, right, left), max_scrolls, :left)
132
198
  end
133
199
 
134
200
  # Scrolls to the start of the scrollable container (top on vertical container, left on horizontal)
135
201
  # @return [TestaAppiumDriver::Locator]
136
- def scroll_to_start(deadzone: nil)
137
- _scroll_to_start_or_end(:start, deadzone)
202
+ def scroll_to_start(top: nil, bottom: nil, right: nil, left: nil)
203
+ _scroll_to_start_or_end(:start, _process_deadzone(top, bottom, right, left))
138
204
  end
139
205
 
140
206
  # Scrolls to the end of the scrollable container (bottom on vertical container, right on horizontal)
141
207
  # @return [TestaAppiumDriver::Locator]
142
- def scroll_to_end(deadzone: nil)
143
- _scroll_to_start_or_end(:end, deadzone)
208
+ def scroll_to_end(top: nil, bottom: nil, right: nil, left: nil)
209
+ _scroll_to_start_or_end(:end, _process_deadzone(top, bottom, right, left))
144
210
  end
145
211
 
146
212
 
147
213
  # @return [TestaAppiumDriver::Locator]
148
- def page_down(deadzone: nil)
149
- _page(:down, deadzone)
214
+ def page_down(top: nil, bottom: nil, right: nil, left: nil)
215
+ _page(:down, _process_deadzone(top, bottom, right, left))
150
216
  end
151
217
 
152
218
  # @return [TestaAppiumDriver::Locator]
153
- def page_up(deadzone: nil)
154
- _page(:up, deadzone)
219
+ def page_up(top: nil, bottom: nil, right: nil, left: nil)
220
+ _page(:up, _process_deadzone(top, bottom, right, left))
155
221
  end
156
222
 
157
223
  # @return [TestaAppiumDriver::Locator]
158
- def page_left(deadzone: nil)
159
- _page(:left, deadzone)
224
+ def page_left(top: nil, bottom: nil, right: nil, left: nil)
225
+ _page(:left, _process_deadzone(top, bottom, right, left))
160
226
  end
161
227
 
162
228
  # @return [TestaAppiumDriver::Locator]
163
- def page_right(deadzone: nil)
164
- _page(:right, deadzone)
229
+ def page_right(top: nil, bottom: nil, right: nil, left: nil)
230
+ _page(:right, _process_deadzone(top, bottom, right, left))
165
231
  end
166
232
 
167
233
  # @return [TestaAppiumDriver::Locator]
168
- def fling_down(deadzone: nil)
169
- _fling(:down, deadzone)
234
+ def fling_down(top: nil, bottom: nil, right: nil, left: nil)
235
+ _fling(:down, _process_deadzone(top, bottom, right, left))
170
236
  end
171
237
 
172
238
  # @return [TestaAppiumDriver::Locator]
173
- def fling_up(deadzone: nil)
174
- _fling(:up, deadzone)
239
+ def fling_up(top: nil, bottom: nil, right: nil, left: nil)
240
+ _fling(:up, _process_deadzone(top, bottom, right, left))
175
241
  end
176
242
 
177
243
  # @return [TestaAppiumDriver::Locator]
178
- def fling_left(deadzone: nil)
179
- _fling(:left, deadzone)
244
+ def fling_left(top: nil, bottom: nil, right: nil, left: nil)
245
+ _fling(:left, _process_deadzone(top, bottom, right, left))
180
246
  end
181
247
 
182
248
  # @return [TestaAppiumDriver::Locator]
183
- def fling_right(deadzone: nil)
184
- _fling(:right, deadzone)
249
+ def fling_right(top: nil, bottom: nil, right: nil, left: nil)
250
+ _fling(:right, _process_deadzone(top, bottom, right, left))
251
+ end
252
+
253
+ def drag_up_by(amount)
254
+ drag_by(amount, direction: :top)
255
+ end
256
+
257
+ def drag_down_by(amount)
258
+ drag_by(amount, direction: :bottom)
259
+ end
260
+
261
+ def drag_left_by(amount)
262
+ drag_by(amount, direction: :left)
263
+ end
264
+
265
+ def drag_right_by(amount)
266
+ drag_by(amount, direction: :right)
185
267
  end
186
268
 
187
269
 
@@ -232,6 +314,18 @@ module TestaAppiumDriver
232
314
 
233
315
 
234
316
  private
317
+ def _process_deadzone(top, bottom, right, left)
318
+ deadzone = nil
319
+ if !top.nil? || !bottom.nil? || !right.nil? || !left.nil?
320
+ deadzone = {}
321
+ deadzone[:top] = top unless top.nil?
322
+ deadzone[:bottom] = bottom unless bottom.nil?
323
+ deadzone[:right] = right unless right.nil?
324
+ deadzone[:left] = left unless left.nil?
325
+ end
326
+ deadzone
327
+ end
328
+
235
329
  def _drag_to(x, y)
236
330
  sa = ScrollActions.new(@scrollable_locator,
237
331
  locator: self,
@@ -239,6 +333,8 @@ module TestaAppiumDriver
239
333
  sa.drag_to(x, y)
240
334
  self
241
335
  end
336
+
337
+
242
338
  def _page(direction, deadzone)
243
339
  deadzone = @scrollable_locator.scroll_deadzone if deadzone.nil? && !@scrollable_locator.nil?
244
340
  sa = ScrollActions.new(@scrollable_locator,
@@ -266,7 +362,6 @@ module TestaAppiumDriver
266
362
  sa = ScrollActions.new(@scrollable_locator,
267
363
  locator: self,
268
364
  deadzone: deadzone,
269
- direction: :left,
270
365
  default_scroll_strategy: @default_scroll_strategy)
271
366
  if type == :start
272
367
  sa.scroll_to_start
@@ -293,7 +388,6 @@ module TestaAppiumDriver
293
388
  locator: self,
294
389
  deadzone: deadzone,
295
390
  max_scrolls: max_scrolls,
296
- direction: direction,
297
391
  default_scroll_strategy: @default_scroll_strategy)
298
392
 
299
393
  sa.send("scroll_#{direction}_to")
@@ -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,32 +91,70 @@ 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
- execute.send(method, *args, &block)
109
+ r = execute.send(method, *args, &block)
110
+ @driver.invalidate_cache
111
+ r
82
112
  end
83
113
 
84
114
 
85
115
  # @param [Boolean] skip_cache if true it will skip cache check and store
86
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
87
117
  # @return [Selenium::WebDriver::Element, Array]
88
- def execute(skip_cache: false, force_cache_element: nil)
118
+ def execute(skip_cache: false, force_cache_element: nil, ignore_implicit_wait: false)
89
119
  return force_cache_element unless force_cache_element.nil?
90
120
  # if we are looking for current element, then return from_element
91
121
  # for example when we have driver.element.elements[1].click
92
122
  # elements[2] will be resolved with xpath because we are looking for multiple elements from element
93
123
  # and since we are looking for instance 2, [](instance) method will return new "empty locator"
94
124
  # we are executing click on that "empty locator" so we have to return the instance 2 of elements for the click
95
- 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)
96
128
  return @from_element
97
129
  end
98
130
 
99
131
 
100
132
 
101
- strategy, selector = strategy_and_selector
102
133
 
103
134
 
104
- @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, 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
105
158
  end
106
159
 
107
160
 
@@ -109,12 +162,8 @@ module TestaAppiumDriver
109
162
  # @return [TestaAppiumDriver::Locator]
110
163
  def wait_until_exists(timeout = nil)
111
164
  timeout = @driver.get_timeouts["implicit"] / 1000 if timeout.nil?
112
- start_time = Time.now.to_f
113
- until exists?
114
- raise "wait until exists timeout exceeded" if start_time + timeout < Time.now.to_f
115
- sleep EXISTS_WAIT
116
- end
117
- self
165
+ args = {timeout: timeout}
166
+ _wait(:until, args)
118
167
  end
119
168
 
120
169
 
@@ -122,28 +171,31 @@ module TestaAppiumDriver
122
171
  # @return [TestaAppiumDriver::Locator]
123
172
  def wait_while_exists(timeout = nil)
124
173
  timeout = @driver.get_timeouts["implicit"] / 1000 if timeout.nil?
125
- start_time = Time.now.to_f
126
- while exists?
127
- raise "wait until exists timeout exceeded" if start_time + timeout < Time.now.to_f
128
- sleep EXISTS_WAIT
129
- end
130
- 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)
131
187
  end
132
188
 
133
189
 
134
190
  # all timeouts are disabled before check, and enabled after check
135
191
  # @return [boolean] true if it exists in the page regardless if visible or not
136
192
  def exists?
137
- @driver.disable_wait_for_idle
138
- @driver.disable_implicit_wait
139
193
  found = true
140
194
  begin
141
- execute(skip_cache: true)
195
+ execute(skip_cache: true, ignore_implicit_wait: true)
142
196
  rescue StandardError
143
197
  found = false
144
198
  end
145
- @driver.enable_implicit_wait
146
- @driver.enable_wait_for_idle
147
199
  found
148
200
  end
149
201
 
@@ -177,7 +229,8 @@ module TestaAppiumDriver
177
229
  locator.can_use_id_strategy = false
178
230
  locator
179
231
  else
180
- from_element = self.execute[instance]
232
+ from_element = self.dup
233
+ from_element.index_for_multiple = instance
181
234
  params = {}.merge({single: true, scrollable_locator: @scrollable_locator})
182
235
  #params[:strategy] = FIND_STRATEGY_XPATH
183
236
  #params[:strategy_reason] = "retrieved instance of a array"
@@ -214,7 +267,8 @@ module TestaAppiumDriver
214
267
  xpath: @xpath_selector,
215
268
  scrollable: @scrollable_locator.nil? ? nil : @scrollable_locator.to_s,
216
269
  scroll_orientation: @scroll_orientation,
217
- resolved: strategy_and_selector
270
+ resolved: strategies_and_selectors,
271
+ index_for_multiple: @index_for_multiple
218
272
  }
219
273
  end
220
274
 
@@ -399,6 +453,51 @@ module TestaAppiumDriver
399
453
 
400
454
  private
401
455
 
456
+ def _wait(type, args)
457
+ interval = EXISTS_WAIT
458
+ interval = args[:interval] unless args[:interval].nil?
459
+
460
+ message = "wait #{type} exists timeout exceeded"
461
+ message = args[:message] unless args[:message].nil?
462
+
463
+ if args[:timeout].nil?
464
+ timeout = @driver.get_timeouts["implicit"] / 1000
465
+ else
466
+ timeout = args[:timeout]
467
+ end
468
+
469
+ args.delete(:message)
470
+ args.delete(:interval)
471
+ args.delete(:timeout)
472
+
473
+
474
+
475
+ start_time = Time.now.to_f
476
+ if type == :while
477
+ while exists? && _attributes_match(args)
478
+ raise message if start_time + timeout < Time.now.to_f
479
+ sleep interval
480
+ end
481
+ else
482
+ until exists? && _attributes_match(args)
483
+ raise message if start_time + timeout < Time.now.to_f
484
+ sleep interval
485
+ end
486
+ end
487
+ self
488
+ end
489
+
490
+ def _attributes_match(attributes)
491
+ all_match = true
492
+ attributes.each do |key, value|
493
+ unless attribute(key) == value
494
+ all_match = false
495
+ break
496
+ end
497
+ end
498
+ all_match
499
+ end
500
+
402
501
  #noinspection RubyNilAnalysis
403
502
  def perform_driver_method(name, *args)
404
503
  elements = execute
@@ -413,6 +512,42 @@ module TestaAppiumDriver
413
512
  locator.single = false unless single # switching from single result to multiple
414
513
  locator.xpath_selector += hash_to_xpath(@driver.device, selectors, single)
415
514
  end
515
+
516
+
517
+ def handle_image_selector(selectors, params)
518
+ image_match_threshold = 0.4
519
+ image_match_threshold = params[:imageMatchThreshold] unless params[:imageMatchThreshold].nil?
520
+ image_match_threshold = params[:threshold] unless params[:threshold].nil?
521
+ fix_image_find_screenshot_dims = true
522
+ fix_image_find_screenshot_dims = params[:fixImageFindScreenshotDims] unless params[:fixImageFindScreenshotDims].nil?
523
+ fix_image_template_size = false
524
+ fix_image_template_size = params[:fixImageTemplateSize] unless params[:fixImageTemplateSize].nil?
525
+ fix_image_template_scale = false
526
+ fix_image_template_scale = params[:fixImageTemplateScale] unless params[:fixImageTemplateScale].nil?
527
+ default_image_template_scale = 1.0
528
+ default_image_template_scale = params[:defaultImageTemplateScale] unless params[:defaultImageTemplateScale].nil?
529
+ check_for_image_element_staleness = true
530
+ check_for_image_element_staleness = params[:checkForImageElementStaleness] unless params[:checkForImageElementStaleness].nil?
531
+ auto_update_image_element_position = false
532
+ auto_update_image_element_position = params[:autoUpdateImageElementPosition] unless params[:autoUpdateImageElementPosition].nil?
533
+ image_element_tap_strategy = "w3cActions"
534
+ image_element_tap_strategy = params[:imageElementTapStrategy] unless params[:imageElementTapStrategy].nil?
535
+ get_matched_image_result = false
536
+ get_matched_image_result = params[:getMatchedImageResult] unless params[:getMatchedImageResult].nil?
537
+
538
+ @image_selector = {
539
+ image: selectors[:image],
540
+ imageMatchThreshold: image_match_threshold,
541
+ fixImageFindScreenshotDims: fix_image_find_screenshot_dims,
542
+ fixImageTemplateSize: fix_image_template_size,
543
+ fixImageTemplateScale: fix_image_template_scale,
544
+ defaultImageTemplateScale: default_image_template_scale,
545
+ checkForImageElementStaleness: check_for_image_element_staleness,
546
+ autoUpdateImageElementPosition: auto_update_image_element_position,
547
+ imageElementTapStrategy: image_element_tap_strategy,
548
+ getMatchedImageResult: get_matched_image_result,
549
+ }
550
+ end
416
551
  end
417
552
 
418
553
  end