appium_lib 9.5.0 → 9.6.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 (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