appium_lib 9.15.1 → 9.15.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -15
  3. data/CHANGELOG.md +8 -0
  4. data/appium_lib.gemspec +7 -10
  5. data/docs/android_docs.md +259 -181
  6. data/docs/ios_docs.md +322 -244
  7. data/docs/ios_xcuitest.md +1 -1
  8. data/docs/w3c.md +21 -0
  9. data/lib/appium_lib/android/common/helper.rb +13 -13
  10. data/lib/appium_lib/android/element/button.rb +2 -0
  11. data/lib/appium_lib/android/element/generic.rb +2 -2
  12. data/lib/appium_lib/android/element/text.rb +2 -0
  13. data/lib/appium_lib/android/element/textfield.rb +2 -0
  14. data/lib/appium_lib/android/uiautomator2/element/button.rb +3 -0
  15. data/lib/appium_lib/android/uiautomator2/helper.rb +10 -8
  16. data/lib/appium_lib/appium.rb +4 -0
  17. data/lib/appium_lib/common/helper.rb +9 -8
  18. data/lib/appium_lib/common/multi_touch.rb +2 -0
  19. data/lib/appium_lib/common/touch_actions.rb +6 -5
  20. data/lib/appium_lib/driver.rb +72 -19
  21. data/lib/appium_lib/ios/common/helper.rb +36 -30
  22. data/lib/appium_lib/ios/element/button.rb +2 -0
  23. data/lib/appium_lib/ios/element/generic.rb +1 -0
  24. data/lib/appium_lib/ios/element/text.rb +2 -0
  25. data/lib/appium_lib/ios/element/textfield.rb +5 -2
  26. data/lib/appium_lib/ios/xcuitest/command/gestures.rb +4 -4
  27. data/lib/appium_lib/ios/xcuitest/element/button.rb +2 -0
  28. data/lib/appium_lib/ios/xcuitest/element/generic.rb +1 -0
  29. data/lib/appium_lib/ios/xcuitest/element/text.rb +2 -0
  30. data/lib/appium_lib/ios/xcuitest/element/textfield.rb +3 -0
  31. data/lib/appium_lib/ios/xcuitest/helper.rb +3 -4
  32. data/lib/appium_lib/version.rb +2 -2
  33. data/release_notes.md +10 -0
  34. metadata +39 -53
@@ -5,7 +5,7 @@
5
5
  - How to migrate XCUITest from UIAutomation
6
6
  - [Migrating your iOS tests from UIAutomation](https://github.com/appium/appium/blob/v1.6.2/docs/en/advanced-concepts/migrating-to-xcuitest.md)
7
7
  - Mobile gestures for XCUITest
8
- - [ios-xctest-mobile-gestures](https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios-xctest-mobile-gestures.md)
8
+ - [ios-xctest-mobile-gestures](https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios/ios-xctest-mobile-gestures.md)
9
9
  - Required Appium1.6.4+
10
10
 
11
11
  ## find elements
@@ -15,6 +15,27 @@ driver.action
15
15
  .move_to_location(500, 500).pointer_down(:left)
16
16
  .move_to_location(0, 700)
17
17
  .release.perform
18
+
19
+ # multiple action chains
20
+ f1 = driver.action.add_pointer_input(:touch, 'finger1')
21
+ f1.create_pointer_move(duration: 1, x: 200, y: 500,
22
+ origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
23
+ f1.create_pointer_down(:left)
24
+ f1.create_pause(0.5)
25
+ f1.create_pointer_move(duration: 1, x: 200, y: 200,
26
+ origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
27
+ f1.create_pointer_up(:left)
28
+
29
+ f2 = driver.action.add_pointer_input(:touch, 'finger2')
30
+ f2.create_pointer_move(duration: 1, x: 200, y: 500,
31
+ origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
32
+ f2.create_pointer_down(:left)
33
+ f2.create_pause(0.5)
34
+ f2.create_pointer_move(duration: 1, x: 200, y: 800,
35
+ origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
36
+ f2.create_pointer_up(:left)
37
+
38
+ driver.perform_actions [f1, f2]
18
39
  ```
19
40
 
20
41
  # Note
@@ -8,7 +8,8 @@ module Appium
8
8
  # convert to string to support symbols
9
9
  def filter=(value)
10
10
  # nil and false disable the filter
11
- return @filter = false unless value
11
+ return @filter = false unless value # rubocop:disable Lint/ReturnInVoidContext
12
+
12
13
  @filter = value.to_s.downcase
13
14
  end
14
15
 
@@ -136,6 +137,7 @@ module Appium
136
137
  index -= 1 if index >= 0
137
138
  else
138
139
  raise 'Index must be >= 1' unless index >= 1
140
+
139
141
  index -= 1 if index >= 1
140
142
  end
141
143
 
@@ -218,7 +220,7 @@ module Appium
218
220
 
219
221
  if class_name == '*'
220
222
  return "//*[contains(translate(@text,'#{value.upcase}', '#{value}'), '#{value}')" \
221
- " or contains(translate(@content-desc,'#{value.upcase}', '#{value}'), '#{value}')" + r_id + ']'
223
+ " or contains(translate(@content-desc,'#{value.upcase}', '#{value}'), '#{value}')" + r_id + ']'
222
224
  end
223
225
 
224
226
  "//#{class_name}[contains(translate(@text,'#{value.upcase}', '#{value}'), '#{value}')" \
@@ -237,14 +239,14 @@ module Appium
237
239
  value = %("#{value}")
238
240
  if class_name == '*'
239
241
  return (resource_id(value, "new UiSelector().resourceId(#{value});") +
240
- "new UiSelector().descriptionContains(#{value});" \
241
- "new UiSelector().textContains(#{value});")
242
+ "new UiSelector().descriptionContains(#{value});" \
243
+ "new UiSelector().textContains(#{value});")
242
244
  end
243
245
 
244
246
  class_name = %("#{class_name}")
245
247
  resource_id(value, "new UiSelector().className(#{class_name}).resourceId(#{value});") +
246
- "new UiSelector().className(#{class_name}).descriptionContains(#{value});" \
247
- "new UiSelector().className(#{class_name}).textContains(#{value});"
248
+ "new UiSelector().className(#{class_name}).descriptionContains(#{value});" \
249
+ "new UiSelector().className(#{class_name}).textContains(#{value});"
248
250
  end
249
251
 
250
252
  # Find the first element that contains value
@@ -272,9 +274,7 @@ module Appium
272
274
  def string_visible_exact_xpath(class_name, value)
273
275
  r_id = resource_id(value, " or @resource-id='#{value}'")
274
276
 
275
- if class_name == '*'
276
- return "//*[@text='#{value}' or @content-desc='#{value}'" + r_id + ']'
277
- end
277
+ return "//*[@text='#{value}' or @content-desc='#{value}'" + r_id + ']' if class_name == '*'
278
278
 
279
279
  "//#{class_name}[@text='#{value}' or @content-desc='#{value}'" + r_id + ']'
280
280
  end
@@ -289,14 +289,14 @@ module Appium
289
289
 
290
290
  if class_name == '*'
291
291
  return (resource_id(value, "new UiSelector().resourceId(#{value});") +
292
- "new UiSelector().description(#{value});" \
293
- "new UiSelector().text(#{value});")
292
+ "new UiSelector().description(#{value});" \
293
+ "new UiSelector().text(#{value});")
294
294
  end
295
295
 
296
296
  class_name = %("#{class_name}")
297
297
  resource_id(value, "new UiSelector().className(#{class_name}).resourceId(#{value});") +
298
- "new UiSelector().className(#{class_name}).description(#{value});" \
299
- "new UiSelector().className(#{class_name}).text(#{value});"
298
+ "new UiSelector().className(#{class_name}).description(#{value});" \
299
+ "new UiSelector().className(#{class_name}).text(#{value});"
300
300
  end
301
301
 
302
302
  # Find the first element exactly matching value
@@ -26,6 +26,7 @@ module Appium
26
26
  # @return [Array<Button>]
27
27
  def buttons(value = false)
28
28
  return find_elements :uiautomator, _button_visible_selectors unless value
29
+
29
30
  find_elements :uiautomator, _button_contains_string(value)
30
31
  end
31
32
 
@@ -69,6 +70,7 @@ module Appium
69
70
  # @private
70
71
  def raise_no_such_element_if_empty(elements)
71
72
  raise _no_such_element if elements.empty?
73
+
72
74
  elements.first
73
75
  end
74
76
 
@@ -45,7 +45,7 @@ module Appium
45
45
  begin
46
46
  elem = find_element :uiautomator, scroll_uiselector(arg, scrollable_index)
47
47
  return elem
48
- rescue => e
48
+ rescue StandardError => e
49
49
  raise e if index == args.size - 1
50
50
  end
51
51
  end
@@ -63,7 +63,7 @@ module Appium
63
63
  begin
64
64
  elem = find_element :uiautomator, scroll_uiselector(arg, scrollable_index)
65
65
  return elem
66
- rescue => e
66
+ rescue StandardError => e
67
67
  raise e if index == args.size - 1
68
68
  end
69
69
  end
@@ -9,6 +9,7 @@ module Appium
9
9
  # @return [TextView]
10
10
  def text(value)
11
11
  return ele_index TextView, value if value.is_a? Numeric
12
+
12
13
  complex_find_contains TextView, value
13
14
  end
14
15
 
@@ -18,6 +19,7 @@ module Appium
18
19
  # @return [Array<TextView>]
19
20
  def texts(value = false)
20
21
  return tags TextView unless value
22
+
21
23
  complex_finds_contains TextView, value
22
24
  end
23
25
 
@@ -8,6 +8,7 @@ module Appium
8
8
  # @return [EditText]
9
9
  def textfield(value)
10
10
  return ele_index EditText, value if value.is_a? Numeric
11
+
11
12
  complex_find_contains EditText, value
12
13
  end
13
14
 
@@ -17,6 +18,7 @@ module Appium
17
18
  # @return [Array<EditText>]
18
19
  def textfields(value = false)
19
20
  return tags EditText unless value
21
+
20
22
  complex_finds_contains EditText, value
21
23
  end
22
24
 
@@ -15,6 +15,7 @@ module Appium
15
15
 
16
16
  result = find_elements :uiautomator, _button_visible_selectors(index: index)
17
17
  raise _no_such_element if result.empty?
18
+
18
19
  return result[value - 1]
19
20
  end
20
21
 
@@ -28,6 +29,7 @@ module Appium
28
29
  # @return [Array<Button>]
29
30
  def buttons(value = false)
30
31
  return find_elements :uiautomator, _button_visible_selectors unless value
32
+
31
33
  find_elements :uiautomator, _button_contains_string(value)
32
34
  end
33
35
 
@@ -74,6 +76,7 @@ module Appium
74
76
  # @private
75
77
  def raise_no_such_element_if_empty(elements)
76
78
  raise _no_such_element if elements.empty?
79
+
77
80
  elements.first
78
81
  end
79
82
 
@@ -14,14 +14,14 @@ module Appium
14
14
  value = %("#{value}")
15
15
  if class_name == '*'
16
16
  return (resource_id(value, "new UiSelector().resourceId(#{value});") +
17
- "new UiSelector().descriptionContains(#{value});" \
18
- "new UiSelector().textContains(#{value});")
17
+ "new UiSelector().descriptionContains(#{value});" \
18
+ "new UiSelector().textContains(#{value});")
19
19
  end
20
20
 
21
21
  class_name = %("#{class_name}")
22
22
  resource_id(value, "new UiSelector().className(#{class_name}).resourceId(#{value});") +
23
- "new UiSelector().className(#{class_name}).descriptionContains(#{value});" \
24
- "new UiSelector().className(#{class_name}).textContains(#{value});"
23
+ "new UiSelector().className(#{class_name}).descriptionContains(#{value});" \
24
+ "new UiSelector().className(#{class_name}).textContains(#{value});"
25
25
  end
26
26
 
27
27
  # Find the first element that contains value
@@ -31,6 +31,7 @@ module Appium
31
31
  def complex_find_contains(class_name, value)
32
32
  elements = find_elements :uiautomator, string_visible_contains(class_name, value)
33
33
  raise _no_such_element if elements.empty?
34
+
34
35
  elements.first
35
36
  end
36
37
 
@@ -52,14 +53,14 @@ module Appium
52
53
 
53
54
  if class_name == '*'
54
55
  return (resource_id(value, "new UiSelector().resourceId(#{value});") +
55
- "new UiSelector().description(#{value});" \
56
- "new UiSelector().text(#{value});")
56
+ "new UiSelector().description(#{value});" \
57
+ "new UiSelector().text(#{value});")
57
58
  end
58
59
 
59
60
  class_name = %("#{class_name}")
60
61
  resource_id(value, "new UiSelector().className(#{class_name}).resourceId(#{value});") +
61
- "new UiSelector().className(#{class_name}).description(#{value});" \
62
- "new UiSelector().className(#{class_name}).text(#{value});"
62
+ "new UiSelector().className(#{class_name}).description(#{value});" \
63
+ "new UiSelector().className(#{class_name}).text(#{value});"
63
64
  end
64
65
 
65
66
  # Find the first element exactly matching value
@@ -69,6 +70,7 @@ module Appium
69
70
  def complex_find_exact(class_name, value)
70
71
  elements = find_elements :uiautomator, string_visible_exact(class_name, value)
71
72
  raise _no_such_element if elements.empty?
73
+
72
74
  elements.first
73
75
  end
74
76
 
@@ -49,6 +49,7 @@ module Appium
49
49
 
50
50
  toml = opts[:file]
51
51
  raise 'Must pass a capability file which has [caps] and [appium_lib]' unless toml
52
+
52
53
  verbose = opts.fetch :verbose, false
53
54
 
54
55
  Appium::Logger.info "appium settings path: #{toml}" if verbose
@@ -57,6 +58,7 @@ module Appium
57
58
  Appium::Logger.info "Exists? #{toml_exists}" if verbose
58
59
 
59
60
  raise "toml doesn't exist #{toml}" unless toml_exists
61
+
60
62
  require 'tomlrb'
61
63
  Appium::Logger.info "Loading #{toml}" if verbose
62
64
 
@@ -135,6 +137,7 @@ module Appium
135
137
  end
136
138
  else
137
139
  raise 'modules must be a module or an array' unless modules.is_a? Array
140
+
138
141
  target_modules = modules
139
142
  end
140
143
 
@@ -181,6 +184,7 @@ module Appium
181
184
  #
182
185
  def promote_appium_methods(class_array, driver = $driver)
183
186
  raise 'Driver is nil' if driver.nil?
187
+
184
188
  # Wrap single class into an array
185
189
  class_array = [class_array] unless class_array.class == Array
186
190
  # Promote Appium driver methods to class instance methods.
@@ -194,7 +194,8 @@ module Appium
194
194
  # convert to string to support symbols
195
195
  def filter=(value)
196
196
  # nil and false disable the filter
197
- return @filter = false unless value
197
+ return @filter = false unless value # rubocop:disable Lint/ReturnInVoidContext
198
+
198
199
  @filter = value.to_s.downcase
199
200
  end
200
201
 
@@ -211,16 +212,13 @@ module Appium
211
212
 
212
213
  def result
213
214
  @elements_in_order.reduce('') do |r, e|
214
- name = e.delete :name
215
+ name = e.delete :name
215
216
  attr_string = e.reduce('') do |string, attr|
216
- attr_1 = attr[1]
217
- attr_1 = attr_1 ? attr_1.strip : attr_1
218
- string + " #{attr[0]}: #{attr_1}\n"
217
+ attr1 = attr[1] ? attr[1].strip : attr[1]
218
+ "#{string} #{attr[0]}: #{attr1}\n"
219
219
  end
220
220
 
221
- unless attr_string.nil? || attr_string.empty?
222
- r += "\n#{name}\n#{attr_string}"
223
- end
221
+ r.concat "\n#{name}\n#{attr_string}" unless attr_string.nil? || attr_string.empty?
224
222
  r
225
223
  end
226
224
  end
@@ -228,6 +226,7 @@ module Appium
228
226
  def start_element(name, attrs = [])
229
227
  @skip_element = filter && !filter.include?(name.downcase)
230
228
  return if @skip_element
229
+
231
230
  element = { name: name }
232
231
  attrs.each { |a| element[a[0]] = a[1] }
233
232
  @element_stack.push element
@@ -236,12 +235,14 @@ module Appium
236
235
 
237
236
  def end_element(name)
238
237
  return if filter && !filter.include?(name.downcase)
238
+
239
239
  element_index = @element_stack.rindex { |e| e[:name] == name }
240
240
  @element_stack.delete_at element_index
241
241
  end
242
242
 
243
243
  def characters(chars)
244
244
  return if @skip_element
245
+
245
246
  element = @element_stack.last
246
247
  element[:text] = chars
247
248
  end
@@ -63,6 +63,7 @@ module Appium
63
63
  pinch.add top
64
64
  pinch.add bottom
65
65
  return pinch unless auto_perform
66
+
66
67
  pinch.perform
67
68
  end
68
69
 
@@ -105,6 +106,7 @@ module Appium
105
106
  zoom.add top
106
107
  zoom.add bottom
107
108
  return zoom unless auto_perform
109
+
108
110
  zoom.perform
109
111
  end
110
112
 
@@ -36,9 +36,10 @@ module Appium
36
36
  COMPLEX_ACTIONS.each do |action|
37
37
  define_method(action) do |opts|
38
38
  auto_perform = opts.delete(:auto_perform) { |_k| true }
39
- ta = ::Appium::TouchAction.new($driver)
39
+ ta = ::Appium::TouchAction.new($driver)
40
40
  ta.send(action, opts)
41
41
  return ta unless auto_perform
42
+
42
43
  ta.perform
43
44
  end
44
45
  end
@@ -49,10 +50,10 @@ module Appium
49
50
  end
50
51
 
51
52
  def swipe(opts)
52
- start_x = opts.fetch :start_x, 0
53
- start_y = opts.fetch :start_y, 0
54
- end_x = opts.fetch :end_x, 0
55
- end_y = opts.fetch :end_y, 0
53
+ start_x = opts.fetch :start_x, 0
54
+ start_y = opts.fetch :start_y, 0
55
+ end_x = opts.fetch :end_x, 0
56
+ end_y = opts.fetch :end_y, 0
56
57
  duration = opts.fetch :duration, 200
57
58
 
58
59
  if opts[:offset_x]
@@ -199,7 +199,7 @@ module Appium
199
199
  # Save global reference to last created Appium driver for top level methods.
200
200
  $driver = self if global_driver
201
201
 
202
- self # return newly created driver
202
+ self # rubocop:disable Lint/Void # return newly created driver
203
203
  end
204
204
 
205
205
  private
@@ -254,23 +254,25 @@ module Appium
254
254
 
255
255
  # Returns a hash of the driver attributes
256
256
  def driver_attributes
257
+ # rubocop:disable Layout/AlignHash
257
258
  {
258
- caps: @core.caps,
259
- automation_name: @core.automation_name,
260
- custom_url: @core.custom_url,
261
- export_session: @core.export_session,
262
- export_session_path: @core.export_session_path,
263
- default_wait: @core.default_wait,
264
- sauce_username: @sauce.username,
265
- sauce_access_key: @sauce.access_key,
266
- sauce_endpoint: @sauce.endpoint,
267
- port: @core.port,
268
- device: @core.device,
269
- debug: @appium_debug,
270
- listener: @listener,
271
- wait_timeout: @core.wait_timeout,
272
- wait_interval: @core.wait_interval
259
+ caps: @core.caps,
260
+ automation_name: @core.automation_name,
261
+ custom_url: @core.custom_url,
262
+ export_session: @core.export_session,
263
+ export_session_path: @core.export_session_path,
264
+ default_wait: @core.default_wait,
265
+ sauce_username: @sauce.username,
266
+ sauce_access_key: @sauce.access_key,
267
+ sauce_endpoint: @sauce.endpoint,
268
+ port: @core.port,
269
+ device: @core.device,
270
+ debug: @appium_debug,
271
+ listener: @listener,
272
+ wait_timeout: @core.wait_timeout,
273
+ wait_interval: @core.wait_interval
273
274
  }
275
+ # rubocop:enable Layout/AlignHash
274
276
  end
275
277
 
276
278
  def device_is_android?
@@ -328,8 +330,9 @@ module Appium
328
330
  # @return [Boolean]
329
331
  def check_server_version_xcuitest
330
332
  if automation_name_is_xcuitest? &&
331
- !@appium_server_status.empty? &&
332
- (@appium_server_status['build']['version'] < REQUIRED_VERSION_XCUITEST)
333
+ !@appium_server_status.empty? &&
334
+ (@appium_server_status['build']['version'] < REQUIRED_VERSION_XCUITEST)
335
+
333
336
  raise(Appium::Core::Error::NotSupportedAppiumServer,
334
337
  "XCUITest requires Appium version >= #{REQUIRED_VERSION_XCUITEST}")
335
338
  end
@@ -351,6 +354,7 @@ module Appium
351
354
  @core.appium_server_version
352
355
  rescue Selenium::WebDriver::Error::WebDriverError => ex
353
356
  raise ::Appium::Core::Error::ServerError unless ex.message.include?('content-type=""')
357
+
354
358
  # server (TestObject for instance) does not respond to status call
355
359
  {}
356
360
  end
@@ -385,6 +389,7 @@ module Appium
385
389
  # @return [String] APP_PATH as an absolute path
386
390
  def self.absolute_app_path(opts)
387
391
  raise 'opts must be a hash' unless opts.is_a? Hash
392
+
388
393
  caps = opts[:caps] || {}
389
394
  app_path = caps[:app]
390
395
  raise 'absolute_app_path invoked and app is not set!' if app_path.nil? || app_path.empty?
@@ -403,6 +408,7 @@ module Appium
403
408
  def server_url
404
409
  return @core.custom_url if @core.custom_url
405
410
  return @sauce.server_url if @sauce.sauce_server_url?
411
+
406
412
  "http://127.0.0.1:#{@core.port}/wd/hub"
407
413
  end
408
414
 
@@ -517,6 +523,7 @@ module Appium
517
523
  unless e.message.include?('The operation requested is not yet implemented by Espresso driver')
518
524
  raise ::Appium::Core::Error::ServerError
519
525
  end
526
+
520
527
  {}
521
528
  end
522
529
 
@@ -562,7 +569,7 @@ module Appium
562
569
 
563
570
  begin
564
571
  yield # search for element
565
- rescue
572
+ rescue StandardError
566
573
  exists = false # error means it's not there
567
574
  end
568
575
 
@@ -580,6 +587,52 @@ module Appium
580
587
  @driver.execute_script script, *args
581
588
  end
582
589
 
590
+ ###
591
+ # Wrap calling selenium webdrier APIs via ruby_core
592
+ ###
593
+ # Get the window handles of open browser windows
594
+ def execute_async_script(script, *args)
595
+ @driver.execute_async_script script, *args
596
+ end
597
+
598
+ def window_handles
599
+ @driver.window_handles
600
+ end
601
+
602
+ # Get the current window handle
603
+ def window_handle
604
+ @driver.window_handle
605
+ end
606
+
607
+ def navigate
608
+ @driver.navigate
609
+ end
610
+
611
+ def manage
612
+ @driver.manage
613
+ end
614
+
615
+ def get(url)
616
+ @driver.get(url)
617
+ end
618
+
619
+ def current_url
620
+ @driver.current_url
621
+ end
622
+
623
+ def title
624
+ @driver.title
625
+ end
626
+
627
+ # @return [TargetLocator]
628
+ # @see TargetLocator
629
+ def switch_to
630
+ @driver.switch_to
631
+ end
632
+ ###
633
+ # End core
634
+ ###
635
+
583
636
  # Calls @driver.find_elements_with_appium
584
637
  #
585
638
  # @example