testa_appium_driver 0.1.3 → 0.1.7

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