appium_lib 9.5.0 → 9.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/docs/android_docs.md +512 -287
  4. data/docs/ios_docs.md +906 -315
  5. data/docs/ios_xcuitest.md +4 -0
  6. data/lib/appium_lib/android/device.rb +39 -0
  7. data/lib/appium_lib/android/element/button.rb +36 -65
  8. data/lib/appium_lib/android/element/generic.rb +20 -12
  9. data/lib/appium_lib/android/helper.rb +4 -16
  10. data/lib/appium_lib/android/uiautomator2/element/button.rb +110 -0
  11. data/lib/appium_lib/android/uiautomator2/helper.rb +85 -0
  12. data/lib/appium_lib/common/command.rb +9 -1
  13. data/lib/appium_lib/common/helper.rb +5 -4
  14. data/lib/appium_lib/common/patch.rb +21 -5
  15. data/lib/appium_lib/common/version.rb +2 -2
  16. data/lib/appium_lib/common/wait.rb +1 -0
  17. data/lib/appium_lib/device/device.rb +13 -23
  18. data/lib/appium_lib/device/multi_touch.rb +60 -20
  19. data/lib/appium_lib/device/touch_actions.rb +17 -8
  20. data/lib/appium_lib/driver.rb +79 -19
  21. data/lib/appium_lib/ios/element/button.rb +5 -25
  22. data/lib/appium_lib/ios/element/generic.rb +4 -22
  23. data/lib/appium_lib/ios/element/text.rb +5 -25
  24. data/lib/appium_lib/ios/element/textfield.rb +31 -78
  25. data/lib/appium_lib/ios/helper.rb +11 -74
  26. data/lib/appium_lib/ios/mobile_methods.rb +0 -20
  27. data/lib/appium_lib/ios/patch.rb +2 -6
  28. data/lib/appium_lib/ios/xcuitest/device.rb +59 -0
  29. data/lib/appium_lib/ios/xcuitest/element.rb +24 -0
  30. data/lib/appium_lib/ios/xcuitest/element/button.rb +64 -0
  31. data/lib/appium_lib/ios/xcuitest/element/generic.rb +50 -0
  32. data/lib/appium_lib/ios/xcuitest/element/text.rb +61 -0
  33. data/lib/appium_lib/ios/xcuitest/element/textfield.rb +94 -0
  34. data/lib/appium_lib/ios/{xcuitest_gestures.rb → xcuitest/gestures.rb} +10 -23
  35. data/lib/appium_lib/ios/xcuitest/helper.rb +103 -0
  36. data/lib/appium_lib/ios/xcuitest/mobile_methods.rb +23 -0
  37. data/release_notes.md +6 -0
  38. metadata +14 -3
data/docs/ios_xcuitest.md CHANGED
@@ -8,6 +8,10 @@
8
8
  - [ios-xctest-mobile-gestures](https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios-xctest-mobile-gestures.md)
9
9
  - Required Appium1.6.4+
10
10
 
11
+ ## Run tests on Multiple Simulators with Xcode 9
12
+ - https://github.com/appium/appium-xcuitest-driver/tree/master/test/functional/parallel
13
+ - https://github.com/appium/ruby_lib/tree/master/ios_tests/parallel
14
+
11
15
  ## find elements
12
16
  - supported elements by find_element are:
13
17
  - [appium-xcuitest-driver](https://github.com/appium/appium-xcuitest-driver/blob/master/lib/commands/find.js#L17)
@@ -0,0 +1,39 @@
1
+ require 'base64'
2
+
3
+ module Appium
4
+ module Android
5
+ module Device
6
+ extend Forwardable
7
+
8
+ # @!method hide_keyboard
9
+ # Hide the onscreen keyboard
10
+ # @param [String] close_key The name of the key which closes the keyboard.
11
+ # Defaults to 'Done' for iOS(except for XCUITest).
12
+ # @param [Symbol] strategy The symbol of the strategy which closes the keyboard.
13
+ # XCUITest ignore this argument.
14
+ # Default for iOS is `:pressKey`. Default for Android is `:tapOutside`.
15
+ # ```ruby
16
+ # hide_keyboard # Close a keyboard with the 'Done' button
17
+ # hide_keyboard('Finished') # Close a keyboard with the 'Finished' button
18
+ # hide_keyboard(nil, :tapOutside) # Close a keyboard with tapping out side of keyboard
19
+ # ```
20
+
21
+ class << self
22
+ def extended(_mod)
23
+ ::Appium::Device.extend_webdriver_with_forwardable
24
+
25
+ ::Appium::Device.add_endpoint_method(:hide_keyboard) do
26
+ def hide_keyboard(close_key = nil, strategy = nil)
27
+ option = {}
28
+
29
+ option[:key] = close_key if close_key
30
+ option[:strategy] = strategy || :tapOutside # default to pressKey
31
+
32
+ execute :hide_keyboard, {}, option
33
+ end
34
+ end
35
+ end
36
+ end # class << self
37
+ end # module Device
38
+ end # module Android
39
+ end # module Appium
@@ -1,38 +1,8 @@
1
- # UIAButton methods
2
1
  module Appium
3
2
  module Android
4
3
  Button = 'android.widget.Button'.freeze
5
4
  ImageButton = 'android.widget.ImageButton'.freeze
6
5
 
7
- private
8
-
9
- def _button_visible_selectors(opts = {})
10
- button_index = opts.fetch :button_index, false
11
- image_button_index = opts.fetch :image_button_index, false
12
-
13
- if button_index && image_button_index
14
- "new UiSelector().className(#{Button}).instance(#{button_index});" \
15
- "new UiSelector().className(#{ImageButton}).instance(#{image_button_index});"
16
- else
17
- "new UiSelector().className(#{Button});" \
18
- "new UiSelector().className(#{ImageButton});"
19
- end
20
- end
21
-
22
- def _button_exact_string(value)
23
- button = string_visible_exact Button, value
24
- image_button = string_visible_exact ImageButton, value
25
- button + image_button
26
- end
27
-
28
- def _button_contains_string(value)
29
- button = string_visible_contains Button, value
30
- image_button = string_visible_contains ImageButton, value
31
- button + image_button
32
- end
33
-
34
- public
35
-
36
6
  # Find the first button that contains value or by index.
37
7
  # @param value [String, Integer] the value to exactly match.
38
8
  # If int then the button at that index is returned.
@@ -44,21 +14,10 @@ module Appium
44
14
  index = value
45
15
  raise "#{index} is not a valid index. Must be >= 1" if index <= 0
46
16
 
47
- unless automation_name_is_uiautomator2?
48
- return find_element :uiautomator, _button_visible_selectors(index: index)
49
- end
50
-
51
- result = find_elements :uiautomator, _button_visible_selectors(index: index)
52
- raise _no_such_element if result.empty?
53
- return result[value - 1]
17
+ return find_element :uiautomator, _button_visible_selectors(index: index)
54
18
  end
55
19
 
56
- if automation_name_is_uiautomator2?
57
- elements = find_elements :uiautomator, _button_contains_string(value)
58
- raise_no_such_element_if_empty(elements)
59
- else
60
- find_element :uiautomator, _button_contains_string(value)
61
- end
20
+ find_element :uiautomator, _button_contains_string(value)
62
21
  end
63
22
 
64
23
  # Find all buttons containing value.
@@ -73,12 +32,7 @@ module Appium
73
32
  # Find the first button.
74
33
  # @return [Button]
75
34
  def first_button
76
- if automation_name_is_uiautomator2?
77
- elements = find_elements :uiautomator, _button_visible_selectors(button_index: 0, image_button_index: 0)
78
- raise_no_such_element_if_empty(elements)
79
- else
80
- find_element :uiautomator, _button_visible_selectors(button_index: 0, image_button_index: 0)
81
- end
35
+ find_element :uiautomator, _button_visible_selectors(button_index: 0, image_button_index: 0)
82
36
  end
83
37
 
84
38
  # Find the last button.
@@ -91,28 +45,16 @@ module Appium
91
45
  image_button_index = tags(ImageButton).length
92
46
  image_button_index -= 1 if image_button_index > 0
93
47
 
94
- if automation_name_is_uiautomator2?
95
- elements = find_elements :uiautomator,
96
- _button_visible_selectors(button_index: button_index,
97
- image_button_index: image_button_index)
98
- raise_no_such_element_if_empty(elements)
99
- else
100
- find_element :uiautomator,
101
- _button_visible_selectors(button_index: button_index,
102
- image_button_index: image_button_index)
103
- end
48
+ find_element :uiautomator,
49
+ _button_visible_selectors(button_index: button_index,
50
+ image_button_index: image_button_index)
104
51
  end
105
52
 
106
53
  # Find the first button that exactly matches value.
107
54
  # @param value [String] the value to match exactly
108
55
  # @return [Button]
109
56
  def button_exact(value)
110
- if automation_name_is_uiautomator2?
111
- elements = find_elements :uiautomator, _button_exact_string(value)
112
- raise_no_such_element_if_empty(elements)
113
- else
114
- find_element :uiautomator, _button_exact_string(value)
115
- end
57
+ find_element :uiautomator, _button_exact_string(value)
116
58
  end
117
59
 
118
60
  # Find all buttons that exactly match value.
@@ -124,9 +66,38 @@ module Appium
124
66
 
125
67
  private
126
68
 
69
+ # @private
127
70
  def raise_no_such_element_if_empty(elements)
128
71
  raise _no_such_element if elements.empty?
129
72
  elements.first
130
73
  end
74
+
75
+ # @private
76
+ def _button_visible_selectors(opts = {})
77
+ button_index = opts.fetch :button_index, false
78
+ image_button_index = opts.fetch :image_button_index, false
79
+
80
+ if button_index && image_button_index
81
+ "new UiSelector().className(#{Button}).instance(#{button_index});" \
82
+ "new UiSelector().className(#{ImageButton}).instance(#{image_button_index});"
83
+ else
84
+ "new UiSelector().className(#{Button});" \
85
+ "new UiSelector().className(#{ImageButton});"
86
+ end
87
+ end
88
+
89
+ # @private
90
+ def _button_exact_string(value)
91
+ button = string_visible_exact Button, value
92
+ image_button = string_visible_exact ImageButton, value
93
+ button + image_button
94
+ end
95
+
96
+ # @private
97
+ def _button_contains_string(value)
98
+ button = string_visible_contains Button, value
99
+ image_button = string_visible_contains ImageButton, value
100
+ button + image_button
101
+ end
131
102
  end # module Android
132
103
  end # module Appium
@@ -39,12 +39,16 @@ module Appium
39
39
  # @return [Element] the element scrolled to
40
40
  def scroll_to(text, scrollable_index = 0)
41
41
  text = %("#{text}")
42
-
43
- args = scroll_uiselector("new UiSelector().textContains(#{text})", scrollable_index) +
44
- scroll_uiselector("new UiSelector().descriptionContains(#{text})", scrollable_index) +
45
- scroll_uiselector(resource_id(text, "new UiSelector().resourceId(#{text});"), scrollable_index)
46
-
47
- find_element :uiautomator, args
42
+ rid = resource_id(text, "new UiSelector().resourceId(#{text});")
43
+ args = rid.empty? ? ["new UiSelector().textContains(#{text})", "new UiSelector().descriptionContains(#{text})"] : [rid]
44
+ args.each_with_index do |arg, index|
45
+ begin
46
+ elem = find_element :uiautomator, scroll_uiselector(arg, scrollable_index)
47
+ return elem
48
+ rescue => e
49
+ raise e if index == args.size - 1
50
+ end
51
+ end
48
52
  end
49
53
 
50
54
  # Scroll to the first element with the exact target text or description.
@@ -53,12 +57,16 @@ module Appium
53
57
  # @return [Element] the element scrolled to
54
58
  def scroll_to_exact(text, scrollable_index = 0)
55
59
  text = %("#{text}")
56
-
57
- args = scroll_uiselector("new UiSelector().text(#{text})", scrollable_index) +
58
- scroll_uiselector("new UiSelector().description(#{text})", scrollable_index) +
59
- scroll_uiselector(resource_id(text, "new UiSelector().resourceId(#{text});"), scrollable_index)
60
-
61
- find_element :uiautomator, args
60
+ rid = resource_id(text, "new UiSelector().resourceId(#{text});")
61
+ args = rid.empty? ? ["new UiSelector().text(#{text})", "new UiSelector().description(#{text})"] : [rid]
62
+ args.each_with_index do |arg, index|
63
+ begin
64
+ elem = find_element :uiautomator, scroll_uiselector(arg, scrollable_index)
65
+ return elem
66
+ rescue => e
67
+ raise e if index == args.size - 1
68
+ end
69
+ end
62
70
  end
63
71
  end # module Android
64
72
  end # module Appium
@@ -23,7 +23,7 @@ module Appium
23
23
  end
24
24
 
25
25
  # http://nokogiri.org/Nokogiri/XML/SAX/Document.html
26
- def start_element(name, attrs = [])
26
+ def start_element(name, attrs = [], driver = $driver)
27
27
  return if filter && !name.downcase.include?(filter)
28
28
 
29
29
  attributes = {}
@@ -34,7 +34,7 @@ module Appium
34
34
 
35
35
  # scoped to: text resource-id content-desc
36
36
  attributes_values = attributes.values
37
- strings = $driver.lazy_load_strings
37
+ strings = driver.lazy_load_strings
38
38
  id_matches = strings.empty? ? [] : strings.select { |_key, value| attributes_values.include? value }
39
39
 
40
40
  string_ids = nil
@@ -285,13 +285,7 @@ module Appium
285
285
  # @param value [String] the value to search for
286
286
  # @return [Element]
287
287
  def complex_find_contains(class_name, value)
288
- if automation_name_is_uiautomator2?
289
- elements = find_elements :uiautomator, string_visible_contains(class_name, value)
290
- raise _no_such_element if elements.empty?
291
- elements.first
292
- else
293
- find_element :uiautomator, string_visible_contains(class_name, value)
294
- end
288
+ find_element :uiautomator, string_visible_contains(class_name, value)
295
289
  end
296
290
 
297
291
  # Find all elements containing value
@@ -343,13 +337,7 @@ module Appium
343
337
  # @param value [String] the value to search for
344
338
  # @return [Element]
345
339
  def complex_find_exact(class_name, value)
346
- if automation_name_is_uiautomator2?
347
- elements = find_elements :uiautomator, string_visible_exact(class_name, value)
348
- raise _no_such_element if elements.empty?
349
- elements.first
350
- else
351
- find_element :uiautomator, string_visible_exact(class_name, value)
352
- end
340
+ find_element :uiautomator, string_visible_exact(class_name, value)
353
341
  end
354
342
 
355
343
  # Find all elements exactly matching value
@@ -0,0 +1,110 @@
1
+ module Appium
2
+ module Android
3
+ module Uiautomator2
4
+ module Element
5
+ # Find the first button that contains value or by index.
6
+ # @param value [String, Integer] the value to exactly match.
7
+ # If int then the button at that index is returned.
8
+ # @return [Button]
9
+ def button(value)
10
+ # Don't use ele_index because that only works on one element type.
11
+ # Android needs to combine button and image button to match iOS.
12
+ if value.is_a? Numeric
13
+ index = value
14
+ raise "#{index} is not a valid index. Must be >= 1" if index <= 0
15
+
16
+ result = find_elements :uiautomator, _button_visible_selectors(index: index)
17
+ raise _no_such_element if result.empty?
18
+ return result[value - 1]
19
+ end
20
+
21
+ elements = find_elements :uiautomator, _button_contains_string(value)
22
+ raise_no_such_element_if_empty(elements)
23
+ end
24
+
25
+ # Find all buttons containing value.
26
+ # If value is omitted, all buttons are returned.
27
+ # @param value [String] the value to search for
28
+ # @return [Array<Button>]
29
+ def buttons(value = false)
30
+ return find_elements :uiautomator, _button_visible_selectors unless value
31
+ find_elements :uiautomator, _button_contains_string(value)
32
+ end
33
+
34
+ # Find the first button.
35
+ # @return [Button]
36
+ def first_button
37
+ elements = find_elements :uiautomator, _button_visible_selectors(button_index: 0, image_button_index: 0)
38
+ raise_no_such_element_if_empty(elements)
39
+ end
40
+
41
+ # Find the last button.
42
+ # @return [Button]
43
+ def last_button
44
+ # uiautomator index doesn't support last
45
+ # and it's 0 indexed
46
+ button_index = tags(::Appium::Android::Button).length
47
+ button_index -= 1 if button_index > 0
48
+ image_button_index = tags(::Appium::Android::ImageButton).length
49
+ image_button_index -= 1 if image_button_index > 0
50
+
51
+ elements = find_elements :uiautomator,
52
+ _button_visible_selectors(button_index: button_index,
53
+ image_button_index: image_button_index)
54
+ raise_no_such_element_if_empty(elements)
55
+ end
56
+
57
+ # Find the first button that exactly matches value.
58
+ # @param value [String] the value to match exactly
59
+ # @return [Button]
60
+ def button_exact(value)
61
+ elements = find_elements :uiautomator, _button_exact_string(value)
62
+ raise_no_such_element_if_empty(elements)
63
+ end
64
+
65
+ # Find all buttons that exactly match value.
66
+ # @param value [String] the value to match exactly
67
+ # @return [Array<Button>]
68
+ def buttons_exact(value)
69
+ find_elements :uiautomator, _button_exact_string(value)
70
+ end
71
+
72
+ private
73
+
74
+ # @private
75
+ def raise_no_such_element_if_empty(elements)
76
+ raise _no_such_element if elements.empty?
77
+ elements.first
78
+ end
79
+
80
+ # @private
81
+ def _button_visible_selectors(opts = {})
82
+ button_index = opts.fetch :button_index, false
83
+ image_button_index = opts.fetch :image_button_index, false
84
+
85
+ if button_index && image_button_index
86
+ "new UiSelector().className(#{::Appium::Android::Button}).instance(#{button_index});" \
87
+ "new UiSelector().className(#{::Appium::Android::ImageButton}).instance(#{image_button_index});"
88
+ else
89
+ "new UiSelector().className(#{::Appium::Android::Button});" \
90
+ "new UiSelector().className(#{::Appium::Android::ImageButton});"
91
+ end
92
+ end
93
+
94
+ # @private
95
+ def _button_exact_string(value)
96
+ button = string_visible_exact ::Appium::Android::Button, value
97
+ image_button = string_visible_exact ::Appium::Android::ImageButton, value
98
+ button + image_button
99
+ end
100
+
101
+ # @private
102
+ def _button_contains_string(value)
103
+ button = string_visible_contains ::Appium::Android::Button, value
104
+ image_button = string_visible_contains ::Appium::Android::ImageButton, value
105
+ button + image_button
106
+ end
107
+ end # module Element
108
+ end # module Uiautomator2
109
+ end # module Android
110
+ end # module Appium
@@ -0,0 +1,85 @@
1
+ module Appium
2
+ module Android
3
+ module Uiautomator2
4
+ module Helper
5
+ # Returns a string that matches the first element that contains value
6
+ # For automationName is Appium
7
+ # example: string_visible_contains 'UIATextField', 'sign in'
8
+ # note for XPath: https://github.com/appium/ruby_lib/pull/561
9
+ #
10
+ # @param class_name [String] the class name for the element
11
+ # @param value [String] the value to search for
12
+ # @return [String]
13
+ def string_visible_contains(class_name, value)
14
+ value = %("#{value}")
15
+ if class_name == '*'
16
+ return (resource_id(value, "new UiSelector().resourceId(#{value});") +
17
+ "new UiSelector().descriptionContains(#{value});" \
18
+ "new UiSelector().textContains(#{value});")
19
+ end
20
+
21
+ class_name = %("#{class_name}")
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});"
25
+ end
26
+
27
+ # Find the first element that contains value
28
+ # @param class_name [String] the class name for the element
29
+ # @param value [String] the value to search for
30
+ # @return [Element]
31
+ def complex_find_contains(class_name, value)
32
+ elements = find_elements :uiautomator, string_visible_contains(class_name, value)
33
+ raise _no_such_element if elements.empty?
34
+ elements.first
35
+ end
36
+
37
+ # Find all elements containing value
38
+ # @param class_name [String] the class name for the element
39
+ # @param value [String] the value to search for
40
+ # @return [Array<Element>]
41
+ def complex_finds_contains(class_name, value)
42
+ find_elements :uiautomator, string_visible_contains(class_name, value)
43
+ end
44
+
45
+ # @private
46
+ # Create an string to exactly match the first element with target value
47
+ # @param class_name [String] the class name for the element
48
+ # @param value [String] the value to search for
49
+ # @return [String]
50
+ def string_visible_exact(class_name, value)
51
+ value = %("#{value}")
52
+
53
+ if class_name == '*'
54
+ return (resource_id(value, "new UiSelector().resourceId(#{value});") +
55
+ "new UiSelector().description(#{value});" \
56
+ "new UiSelector().text(#{value});")
57
+ end
58
+
59
+ class_name = %("#{class_name}")
60
+ 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});"
63
+ end
64
+
65
+ # Find the first element exactly matching value
66
+ # @param class_name [String] the class name for the element
67
+ # @param value [String] the value to search for
68
+ # @return [Element]
69
+ def complex_find_exact(class_name, value)
70
+ elements = find_elements :uiautomator, string_visible_exact(class_name, value)
71
+ raise _no_such_element if elements.empty?
72
+ elements.first
73
+ end
74
+
75
+ # Find all elements exactly matching value
76
+ # @param class_name [String] the class name for the element
77
+ # @param value [String] the value to search for
78
+ # @return [Element]
79
+ def complex_finds_exact(class_name, value)
80
+ find_elements :uiautomator, string_visible_exact(class_name, value)
81
+ end
82
+ end # module Helper
83
+ end # module Uiautomator2
84
+ end # module Android
85
+ end # module Appium