appium_lib 8.2.1 → 9.0.0

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/android_tests/lib/android/specs/common/device_touchaction.rb +2 -2
  3. data/appium_lib.gemspec +2 -2
  4. data/docs/android_docs.md +245 -212
  5. data/docs/docs.md +17 -15
  6. data/docs/index_paths.md +2 -2
  7. data/docs/ios_docs.md +422 -240
  8. data/docs/ios_xcuitest.md +25 -0
  9. data/ios_tests/Gemfile +2 -0
  10. data/ios_tests/appium.txt +2 -1
  11. data/ios_tests/lib/common.rb +98 -4
  12. data/ios_tests/lib/ios/specs/common/helper.rb +24 -28
  13. data/ios_tests/lib/ios/specs/common/patch.rb +1 -1
  14. data/ios_tests/lib/ios/specs/device/device.rb +17 -11
  15. data/ios_tests/lib/ios/specs/device/multi_touch.rb +22 -1
  16. data/ios_tests/lib/ios/specs/device/touch_actions.rb +14 -5
  17. data/ios_tests/lib/ios/specs/driver.rb +13 -9
  18. data/ios_tests/lib/ios/specs/ios/element/alert.rb +12 -8
  19. data/ios_tests/lib/ios/specs/ios/element/button.rb +6 -3
  20. data/ios_tests/lib/ios/specs/ios/element/text.rb +5 -3
  21. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +12 -8
  22. data/ios_tests/lib/ios/specs/ios/helper.rb +9 -3
  23. data/ios_tests/lib/ios/specs/ios/patch.rb +9 -1
  24. data/ios_tests/readme.md +3 -2
  25. data/lib/appium_lib/common/error.rb +5 -0
  26. data/lib/appium_lib/common/version.rb +2 -2
  27. data/lib/appium_lib/device/device.rb +7 -1
  28. data/lib/appium_lib/device/multi_touch.rb +27 -9
  29. data/lib/appium_lib/device/touch_actions.rb +12 -5
  30. data/lib/appium_lib/driver.rb +29 -1
  31. data/lib/appium_lib/ios/element/button.rb +50 -24
  32. data/lib/appium_lib/ios/element/generic.rb +20 -4
  33. data/lib/appium_lib/ios/element/text.rb +48 -24
  34. data/lib/appium_lib/ios/element/textfield.rb +80 -40
  35. data/lib/appium_lib/ios/helper.rb +107 -33
  36. data/lib/appium_lib/ios/mobile_methods.rb +1 -0
  37. data/lib/appium_lib/ios/patch.rb +5 -2
  38. data/readme.md +1 -0
  39. data/release_notes.md +10 -0
  40. metadata +16 -2
@@ -3,18 +3,18 @@ describe 'ios/element/alert' do
3
3
  def nav_once
4
4
  screen.must_equal catalog
5
5
  wait_true do
6
- text('alerts').click
7
- tag('UIANavigationBar').name == 'Alerts' # wait for true
6
+ UI::Inventory.xcuitest? ? find_element(:name, 'Alerts').click : text('alerts').click
7
+ tag(UI::Inventory.navbar).name == 'Alerts' # wait for true
8
8
  end
9
9
 
10
- tag('UIANavigationBar').name.must_equal 'Alerts'
11
-
12
10
  # redefine method as no-op after it's invoked once
13
11
  self.class.send :define_method, :nav_once, proc {}
14
12
  end
15
13
 
16
14
  def after_last
17
- alert_accept if exists { text('UIActionSheet <title>') }
15
+ alert_accept if exists do
16
+ UI::Inventory.xcuitest? ? find_elements(:name, 'UIActionSheet <title>') : text('UIActionSheet <title>')
17
+ end
18
18
  back_click
19
19
  screen.must_equal catalog
20
20
  sleep 1
@@ -27,9 +27,13 @@ describe 'ios/element/alert' do
27
27
 
28
28
  def open_alert
29
29
  wait_true do
30
- return true if exists { text('UIActionSheet <title>') }
31
- text('Show OK-Cancel').click
32
- text('UIActionSheet <title>').displayed?
30
+ if UI::Inventory.xcuitest?
31
+ find_element(:name, 'Show OK-Cancel').click
32
+ find_element(:name, 'UIActionSheet <title>').displayed?
33
+ else
34
+ text('Show OK-Cancel').click
35
+ text('UIActionSheet <title>').displayed?
36
+ end
33
37
  end
34
38
  end
35
39
 
@@ -3,7 +3,7 @@ describe 'ios/element/button' do
3
3
  def before_first
4
4
  screen.must_equal catalog
5
5
  # nav to buttons activity
6
- wait { text('buttons').click }
6
+ wait { UI::Inventory.xcuitest? ? find_element(:name, 'Buttons').click : text('buttons').click }
7
7
  end
8
8
 
9
9
  def after_last
@@ -21,7 +21,7 @@ describe 'ios/element/button' do
21
21
 
22
22
  t 'button' do
23
23
  # by index
24
- button(3).name.must_equal gray
24
+ button(3).name.must_equal gray # or UIButtonTypeInfoDark in XCUITest
25
25
 
26
26
  # by name contains
27
27
  button('ray').name.must_equal gray
@@ -29,6 +29,8 @@ describe 'ios/element/button' do
29
29
 
30
30
  t 'buttons' do
31
31
  exp = ['Back', 'Back', 'Gray', 'Right pointing arrow']
32
+ exp.concat ['Custom Text', 'More info', 'More info', 'More info', 'Add contact'] if UI::Inventory.xcuitest?
33
+
32
34
  target_buttons = buttons('a')
33
35
  target_buttons.map(&:name).must_equal exp
34
36
  target_buttons.length.must_equal exp.length
@@ -39,7 +41,8 @@ describe 'ios/element/button' do
39
41
  end
40
42
 
41
43
  t 'last_button' do
42
- last_button.name.must_equal 'Rounded'
44
+ expected = UI::Inventory.xcuitest? ? 'Add contact' : 'Rounded'
45
+ last_button.name.must_equal expected
43
46
  end
44
47
 
45
48
  t 'button_exact' do
@@ -23,8 +23,10 @@ describe 'ios/element/text' do
23
23
  end
24
24
 
25
25
  t 'last_text' do
26
- last_text.text.must_equal uiview_transitions
27
- last_text.name.must_equal uiview_transitions
26
+ expected = UI::Inventory.xcuitest? ? 'Shows UIViewAnimationTransitions' : 'Transitions'
27
+
28
+ last_text.text.must_equal expected
29
+ last_text.name.must_equal expected
28
30
  end
29
31
 
30
32
  t 'text' do
@@ -35,7 +37,7 @@ describe 'ios/element/text' do
35
37
 
36
38
  t 'texts' do
37
39
  exp = ['Controls', 'Various uses of UIControl', 'Various uses of UISegmentedControl']
38
- texts.length.must_equal 24
40
+ texts.length.must_equal(UI::Inventory.xcuitest? ? 25 : 24)
39
41
  texts('trol').map(&:name).must_equal exp
40
42
  texts('uses').length.must_equal 7
41
43
  end
@@ -34,6 +34,8 @@ describe 'ios/element/textfield' do
34
34
  end
35
35
 
36
36
  t 'predicate textfields' do
37
+ fail NotImplementedError, "XCUITest(Appium1.6.2) doesn't support UIAutomation script" if UI::Inventory.xcuitest?
38
+
37
39
  textfield_count = execute_script(%(au.mainApp().getAllWithPredicate("type contains[c] 'textfield'", true))).length
38
40
  textfield_count.must_equal 4
39
41
  end
@@ -68,15 +70,17 @@ describe 'ios/element/textfield' do
68
70
 
69
71
  t 'textfield type' do
70
72
  # Regular send keys triggers the keyboard and doesn't dismiss
71
- keyboard_must_not_exist
73
+ keyboard_must_not_exist unless UI::Inventory.xcuitest? # xcuitest doesn't support JS command
72
74
  textfield(1).send_keys 'ok'
73
- keyboard_must_exist
74
-
75
- # type should not dismiss the keyboard
76
- message = 'type test type'
77
- textfield(1).type message
78
- keyboard_must_exist
79
- textfield(1).text.must_equal message
75
+ keyboard_must_exist unless UI::Inventory.xcuitest? # xcuitest doesn't support JS command
76
+
77
+ unless UI::Inventory.xcuitest?
78
+ # type should not dismiss the keyboard
79
+ message = 'type test type'
80
+ textfield(1).type message
81
+ keyboard_must_exist
82
+ textfield(1).text.must_equal message
83
+ end
80
84
  end
81
85
 
82
86
  def must_raise_no_element(&block)
@@ -18,10 +18,16 @@ describe 'ios/helper' do
18
18
  # t 'page' do # writes to std out
19
19
 
20
20
  t 'source_window' do
21
- source_window.length.must_equal 11
21
+ source_window.length.must_be :>=, 14_000
22
22
  end
23
23
 
24
24
  # TODO: t 'page_window' do
25
- # TODO: t 'id' do
26
- # TODO: t 'ios_version' do
25
+
26
+ t 'id' do
27
+ id 'Buttons' # 'Various uses of UIButton'
28
+ end
29
+
30
+ t 'ios_version' do
31
+ ios_version.wont_be_empty
32
+ end
27
33
  end
@@ -13,13 +13,21 @@ describe 'ios/patch' do
13
13
  before_first
14
14
  end
15
15
 
16
- # TODO: test 'label'
16
+ t 'label' do
17
+ if UI::Inventory.xcuitest?
18
+ # Order of the elements are: Normal, Rounded, Secure, Check
19
+ find_element(:name, '<enter text>').label.must_equal 'Normal'
20
+ else
21
+ textfield('<enter text>').label.must_equal 'Rounded'
22
+ end
23
+ end
17
24
 
18
25
  t 'type' do
19
26
  # nav to textfield
20
27
  text('textfields').click
21
28
 
22
29
  ele = first_textfield
30
+
23
31
  ele.type 'ok'
24
32
  ele.text.must_equal 'ok'
25
33
  end
@@ -12,12 +12,13 @@ ruby_lib's iOS tests. Requires `Ruby 1.9.3` or better.
12
12
 
13
13
  `UICatalog6.1` is from [appium/appium](https://github.com/appium/appium/blob/master/assets/UICatalog6.1.app.zip)
14
14
 
15
- The tests are now run against `iPhone 6 Simulator 9.3 (12F69)`
15
+ There are two backend drivers available for iOS (automationName). UIAutomation which is supported up-to iOS 9.3 and XCUITest which is supported from 10.0 on. UIAutomation is deprecated but will continue to be supported for the time being.
16
16
 
17
+ By default, the tests are now run against `iPhone 6 Simulator 10.1 (14A345)`
17
18
  #### Documentation
18
19
 
19
20
  - [Installing Appium on OS X](https://github.com/appium/ruby_console/blob/master/osx.md)
20
- - [Overview](https://github.com/appium/ruby_lib/blob/master/docs/docs.md)
21
+ - [Overview](https://github.com/appium/ruby_lib/blob/master/docs/docs.md)
21
22
  - [iOS methods](https://github.com/appium/ruby_lib/blob/master/docs/ios_docs.md)
22
23
  - [Minitest Expectations](http://ruby-doc.org/stdlib-1.9.3/libdoc/minitest/spec/rdoc/MiniTest/Expectations.html)
23
24
 
@@ -0,0 +1,5 @@
1
+ module Appium
2
+ module Error
3
+ class NotSupportedAppiumServer < RuntimeError; end
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  module Appium
2
2
  # Version and Date are defined on the 'Appium' module, not 'Appium::Common'
3
- VERSION = '8.2.1' unless defined? ::Appium::VERSION
4
- DATE = '2016-11-29' unless defined? ::Appium::DATE
3
+ VERSION = '9.0.0' unless defined? ::Appium::VERSION
4
+ DATE = '2016-12-09' unless defined? ::Appium::DATE
5
5
  end
@@ -212,7 +212,13 @@ module Appium
212
212
  end
213
213
 
214
214
  close_key ||= 'Done' # default to Done key.
215
- $driver.hide_ios_keyboard close_key
215
+ if $driver.automation_name_is_xcuitest?
216
+ # strategy is not implemented in the following
217
+ # https://github.com/appium/appium-xcuitest-driver/blob/v2.2.0/lib/commands/general.js#L51
218
+ execute :hide_keyboard, {}, strategy: :grouped, key: close_key
219
+ else
220
+ $driver.hide_ios_keyboard close_key
221
+ end
216
222
  end
217
223
  end
218
224
 
@@ -29,13 +29,21 @@ module Appium
29
29
  fail ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
30
30
 
31
31
  p = Float(percentage) / 100
32
- i = 1 - p
33
32
 
34
- top = TouchAction.new
35
- top.swipe start_x: 1.0, start_y: 0.0, delta_x: i, delta_y: i, duration: 1
33
+ if $driver.automation_name_is_xcuitest?
34
+ ele = $driver.find_element :class, 'XCUIElementTypeApplication'
35
+ top = TouchAction.new
36
+ top.swipe({ start_x: 1.0, start_y: 0.0, delta_x: -p, delta_y: p }, ele)
37
+
38
+ bottom = TouchAction.new
39
+ bottom.swipe({ start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: -p }, ele)
40
+ else
41
+ top = TouchAction.new
42
+ top.swipe start_x: 1.0, start_y: 0.0, delta_x: -p, delta_y: p, duration: 1
36
43
 
37
- bottom = TouchAction.new
38
- bottom.swipe(start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: p, duration: 1)
44
+ bottom = TouchAction.new
45
+ bottom.swipe start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: -p, duration: 1
46
+ end
39
47
 
40
48
  pinch = MultiTouch.new
41
49
  pinch.add top
@@ -59,11 +67,21 @@ module Appium
59
67
  p = 100 / Float(percentage)
60
68
  i = 1 - p
61
69
 
62
- top = TouchAction.new
63
- top.swipe start_x: i, start_y: i, delta_x: 1, delta_y: 1, duration: 1
70
+ if $driver.automation_name_is_xcuitest?
71
+ ele = $driver.find_element :class, 'XCUIElementTypeApplication'
72
+
73
+ top = TouchAction.new
74
+ top.swipe({ start_x: p, start_y: i, delta_x: i, delta_y: -i }, ele)
75
+
76
+ bottom = TouchAction.new
77
+ bottom.swipe({ start_x: i, start_y: p, delta_x: -i, delta_y: i }, ele)
78
+ else
79
+ top = TouchAction.new
80
+ top.swipe start_x: p, start_y: i, delta_x: i, delta_y: -i, duration: 1
64
81
 
65
- bottom = TouchAction.new
66
- bottom.swipe start_x: p, start_y: p, delta_x: 1, delta_y: 1, duration: 1
82
+ bottom = TouchAction.new
83
+ bottom.swipe start_x: i, start_y: p, delta_x: -i, delta_y: i, duration: 1
84
+ end
67
85
 
68
86
  zoom = MultiTouch.new
69
87
  zoom.add top
@@ -124,7 +124,7 @@ module Appium
124
124
  # @option opts [int] :delta_y The distance from start to move, on the y axis. Default 0.
125
125
  # @option opts [int] :duration How long the actual swipe takes to complete in milliseconds. Default 200.
126
126
  # @deprecated Please do not use end_x, end_y anymore
127
- def swipe(opts)
127
+ def swipe(opts, ele = nil)
128
128
  start_x = opts.fetch :start_x, 0
129
129
  start_y = opts.fetch :start_y, 0
130
130
  delta_x = opts.fetch :delta_x, nil
@@ -144,10 +144,17 @@ module Appium
144
144
 
145
145
  duration = opts.fetch :duration, 200
146
146
 
147
- press x: start_x, y: start_y
148
- wait(duration) if duration
149
- move_to x: start_x + delta_x, y: start_y + delta_y
150
- release
147
+ if ele # pinch/zoom for XCUITest
148
+ press x: start_x, y: start_y, element: ele
149
+ move_to x: start_x + delta_x, y: start_y + delta_y, element: ele
150
+ release
151
+ else
152
+ press x: start_x, y: start_y
153
+ wait(duration) if duration
154
+ move_to x: start_x + delta_x, y: start_y + delta_y
155
+ release
156
+ end
157
+
151
158
  self
152
159
  end
153
160
 
@@ -9,6 +9,7 @@ require_relative 'common/helper'
9
9
  require_relative 'common/wait'
10
10
  require_relative 'common/patch'
11
11
  require_relative 'common/version'
12
+ require_relative 'common/error'
12
13
  require_relative 'common/element/window'
13
14
 
14
15
  # ios
@@ -50,6 +51,8 @@ module Minitest
50
51
  end
51
52
 
52
53
  module Appium
54
+ REQUIRED_VERSION_XCUITEST = '1.6.0'.freeze
55
+
53
56
  # Load arbitrary text (toml format)
54
57
  #
55
58
  # ```
@@ -265,11 +268,14 @@ module Appium
265
268
  attr_accessor :appium_port
266
269
  # Device type to request from the appium server
267
270
  attr_accessor :appium_device
271
+ # Automation name sent to appium server
272
+ attr_reader :automation_name
273
+ # Appium's server version
274
+ attr_reader :appium_server_version
268
275
  # Boolean debug mode for the Appium Ruby bindings
269
276
  attr_accessor :appium_debug
270
277
  # instance of AbstractEventListener for logging support
271
278
  attr_accessor :listener
272
-
273
279
  # Returns the driver
274
280
  # @return [Driver] the driver
275
281
  attr_reader :driver
@@ -329,6 +335,8 @@ module Appium
329
335
  @appium_device = @caps[:platformName]
330
336
  @appium_device = @appium_device.is_a?(Symbol) ? @appium_device : @appium_device.downcase.strip.intern if @appium_device
331
337
 
338
+ @automation_name = @caps[:automationName] if @caps[:automationName]
339
+
332
340
  # load common methods
333
341
  extend Appium::Common
334
342
  extend Appium::Device
@@ -386,6 +394,22 @@ module Appium
386
394
  @appium_device == :android
387
395
  end
388
396
 
397
+ # Return true if automationName is 'XCUITest'
398
+ # @return [Boolean]
399
+ def automation_name_is_xcuitest?
400
+ !@automation_name.nil? && @automation_name.downcase == 'xcuitest'
401
+ end
402
+
403
+ # Return true if the target Appium server is over REQUIRED_VERSION_XCUITEST.
404
+ # If the Appium server is under REQUIRED_VERSION_XCUITEST, then error is raised.
405
+ # @return [Boolean]
406
+ def check_server_version_xcuitest
407
+ if automation_name_is_xcuitest? && (@appium_server_version['build']['version'] <= REQUIRED_VERSION_XCUITEST)
408
+ fail Appium::Error::NotSupportedAppiumServer, "XCUITest requires over Appium #{REQUIRED_VERSION_XCUITEST}"
409
+ end
410
+ true
411
+ end
412
+
389
413
  # Returns the server's version info
390
414
  #
391
415
  # ```ruby
@@ -508,6 +532,10 @@ module Appium
508
532
  raise "ERROR: Unable to connect to Appium. Is the server running on #{server_url}?"
509
533
  end
510
534
 
535
+ @appium_server_version = appium_server_version
536
+
537
+ check_server_version_xcuitest
538
+
511
539
  @driver.manage.timeouts.implicit_wait = @default_wait
512
540
 
513
541
  @driver
@@ -1,51 +1,77 @@
1
- # UIAButton methods
1
+ # XCUIElementTypeButton methods
2
2
  module Appium
3
3
  module Ios
4
- UIAButton = 'UIAButton'
4
+ UIAButton = 'UIAButton'.freeze
5
+ XCUIElementTypeButton = 'XCUIElementTypeButton'.freeze
5
6
 
6
- # Find the first UIAButton that contains value or by index.
7
+ # @return [String] Class name for button
8
+ def button_class
9
+ automation_name_is_xcuitest? ? XCUIElementTypeButton : UIAButton
10
+ end
11
+
12
+ # Find the first UIAButton|XCUIElementTypeButton that contains value or by index.
7
13
  # @param value [String, Integer] the value to exactly match.
8
- # If int then the UIAButton at that index is returned.
9
- # @return [UIAButton]
14
+ # If int then the UIAButton|XCUIElementTypeButton at that index is returned.
15
+ # @return [UIAButton|XCUIElementTypeButton]
10
16
  def button(value)
11
17
  # return button at index.
12
- return ele_index UIAButton, value if value.is_a? Numeric
13
- ele_by_json_visible_contains UIAButton, value
18
+ return ele_index button_class, value if value.is_a? Numeric
19
+
20
+ if automation_name_is_xcuitest?
21
+ find_ele_by_attr_include button_class, '*', value
22
+ else
23
+ ele_by_json_visible_contains button_class, value
24
+ end
14
25
  end
15
26
 
16
- # Find all UIAButtons containing value.
17
- # If value is omitted, all UIAButtons are returned.
27
+ # Find all UIAButtons|XCUIElementTypeButtons containing value.
28
+ # If value is omitted, all UIAButtons|XCUIElementTypeButtons are returned.
18
29
  # @param value [String] the value to search for
19
- # @return [Array<UIAButton>]
30
+ # @return [Array<UIAButton|XCUIElementTypeButton>]
20
31
  def buttons(value = false)
21
- return tags UIAButton unless value
22
- eles_by_json_visible_contains UIAButton, value
32
+ return tags button_class unless value
33
+
34
+ if automation_name_is_xcuitest?
35
+ find_eles_by_attr_include button_class, '*', value
36
+ else
37
+ eles_by_json_visible_contains button_class, value
38
+ end
23
39
  end
24
40
 
25
- # Find the first UIAButton.
26
- # @return [UIAButton]
41
+ # Find the first UIAButton|XCUIElementTypeButton.
42
+ # @return [UIAButton|XCUIElementTypeButton]
27
43
  def first_button
28
- first_ele UIAButton
44
+ first_ele button_class
29
45
  end
30
46
 
31
- # Find the last UIAButton.
32
- # @return [UIAButton]
47
+ # TODO: add documentation regarding previous element.
48
+ # Previous UIAElement is differ from UIAButton|XCUIElementTypeButton. So, the results are different.
49
+ # Find the last UIAButton|XCUIElementTypeButton.
50
+ # @return [UIAButton|XCUIElementTypeButton]
33
51
  def last_button
34
- last_ele UIAButton
52
+ last_ele button_class
35
53
  end
36
54
 
37
- # Find the first UIAButton that exactly matches value.
55
+ # Find the first UIAButton|XCUIElementTypeButton that exactly matches value.
38
56
  # @param value [String] the value to match exactly
39
- # @return [UIAButton]
57
+ # @return [UIAButton|XCUIElementTypeButton]
40
58
  def button_exact(value)
41
- ele_by_json_visible_exact UIAButton, value
59
+ if automation_name_is_xcuitest?
60
+ find_ele_by_attr button_class, '*', value
61
+ else
62
+ ele_by_json_visible_exact button_class, value
63
+ end
42
64
  end
43
65
 
44
- # Find all UIAButtons that exactly match value.
66
+ # Find all UIAButtons|XCUIElementTypeButtons that exactly match value.
45
67
  # @param value [String] the value to match exactly
46
- # @return [Array<UIAButton>]
68
+ # @return [Array<UIAButton|XCUIElementTypeButton>]
47
69
  def buttons_exact(value)
48
- eles_by_json_visible_exact UIAButton, value
70
+ if automation_name_is_xcuitest?
71
+ find_eles_by_attr button_class, '*', value
72
+ else
73
+ eles_by_json_visible_exact button_class, value
74
+ end
49
75
  end
50
76
  end # module Ios
51
77
  end # module Appium