appium_lib 8.2.1 → 9.0.0

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