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
@@ -21,25 +21,5 @@ module Appium
21
21
  ::Appium::Driver::SearchContext::FINDERS[:predicate] = '-ios predicate string'
22
22
  end
23
23
  end # class << self
24
-
25
- module Xcuitest
26
- class << self
27
- # @!method ios_class_chain_find
28
- # Only for XCUITest(WebDriverAgent)
29
- # find_element/s can be used with a [class chain]( https://github.com/facebook/WebDriverAgent/wiki/Queries)
30
- #
31
- # ```ruby
32
- # # select the third child button of the first child window element
33
- # find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]'
34
- # # select all the children windows
35
- # find_elements :class_chain, 'XCUIElementTypeWindow'
36
- # # select the second last child of the second child window
37
- # find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]'
38
- # ```
39
- def extended(_mod)
40
- ::Appium::Driver::SearchContext::FINDERS[:class_chain] = '-ios class chain'
41
- end
42
- end
43
- end
44
24
  end # module Ios
45
25
  end # module Appium
@@ -13,12 +13,8 @@ module Appium
13
13
  end
14
14
 
15
15
  # Cross platform way of entering text into a textfield
16
- def type(text)
17
- if $driver.automation_name_is_xcuitest?
18
- send_keys text
19
- else
20
- $driver.execute_script %(au.getElement('#{ref}').setValue('#{text}');)
21
- end
16
+ def type(text, driver = $driver)
17
+ driver.execute_script %(au.getElement('#{ref}').setValue('#{text}');)
22
18
  end # def type
23
19
  end # Selenium::WebDriver::Element.class_eval
24
20
  end # def patch_webdriver_element
@@ -0,0 +1,59 @@
1
+ require 'base64'
2
+
3
+ module Appium
4
+ module Ios
5
+ module Xcuitest
6
+ module Device
7
+ extend Forwardable
8
+
9
+ # @!method hide_keyboard
10
+ # Hide the onscreen keyboard
11
+ # @param [String] close_key The name of the key which closes the keyboard.
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
+ # ```
19
+
20
+ # @!method background_app
21
+ # Backgrounds the app for a set number of seconds.
22
+ # This is a blocking application
23
+ # @param [Integer] seconds How many seconds to background the app for.
24
+ #
25
+ # ```ruby
26
+ # background_app
27
+ # background_app(5)
28
+ # background_app(-1) #=> the app never come back. https://github.com/appium/appium/issues/7741
29
+ # ```
30
+
31
+ class << self
32
+ def extended(_mod)
33
+ ::Appium::Device.extend_webdriver_with_forwardable
34
+
35
+ ::Appium::Device.add_endpoint_method(:hide_keyboard) do
36
+ def hide_keyboard(close_key = nil, strategy = nil)
37
+ option = {}
38
+
39
+ option[:key] = close_key if close_key
40
+ option[:strategy] = strategy if strategy
41
+
42
+ execute :hide_keyboard, {}, option
43
+ end
44
+ end
45
+
46
+ ::Appium::Device.add_endpoint_method(:background_app) do
47
+ def background_app(duration = 0)
48
+ # https://github.com/appium/ruby_lib/issues/500, https://github.com/appium/appium/issues/7741
49
+ # `execute :background_app, {}, seconds: { timeout: duration_milli_sec }` works over Appium 1.6.4
50
+ duration_milli_sec = duration.nil? ? nil : duration * 1000
51
+ execute :background_app, {}, seconds: { timeout: duration_milli_sec }
52
+ end
53
+ end
54
+ end
55
+ end # class << self
56
+ end # module Device
57
+ end # module Xcuitest
58
+ end # module Ios
59
+ end # module Appium
@@ -0,0 +1,24 @@
1
+ module Appium
2
+ module Ios
3
+ module Xcuitest
4
+ # @private
5
+ # class_eval inside a method because class Selenium::WebDriver::Element
6
+ # will trigger as soon as the file is required. in contrast a method
7
+ # will trigger only when invoked.
8
+ def patch_webdriver_element
9
+ Selenium::WebDriver::Element.class_eval do
10
+ # Enable access to iOS accessibility label
11
+ # accessibility identifier is supported as 'name'
12
+ def label
13
+ attribute('label')
14
+ end
15
+
16
+ # Cross platform way of entering text into a textfield
17
+ def type(text)
18
+ send_keys text
19
+ end # def type
20
+ end # Selenium::WebDriver::Element.class_eval
21
+ end # def patch_webdriver_element
22
+ end # module Xcuitest
23
+ end # module Ios
24
+ end # module Appium
@@ -0,0 +1,64 @@
1
+ # XCUIElementTypeButton methods
2
+ module Appium
3
+ module Ios
4
+ module Xcuitest
5
+ module Element
6
+ # XCUIElementTypeButton = 'XCUIElementTypeButton'.freeze
7
+
8
+ # @return [String] Class name for button
9
+ def button_class
10
+ ::Appium::Ios::XCUIElementTypeButton
11
+ end
12
+
13
+ # Find the first UIAButton|XCUIElementTypeButton that contains value or by index.
14
+ # @param value [String, Integer] the value to exactly match.
15
+ # If int then the UIAButton|XCUIElementTypeButton at that index is returned.
16
+ # @return [UIAButton|XCUIElementTypeButton]
17
+ def button(value)
18
+ # return button at index.
19
+ return ele_index button_class, value if value.is_a? Numeric
20
+ raise_error_if_no_element buttons(value).first
21
+ end
22
+
23
+ # Find all UIAButtons|XCUIElementTypeButtons containing value.
24
+ # If value is omitted, all UIAButtons|XCUIElementTypeButtons are returned.
25
+ # @param value [String] the value to search for
26
+ # @return [Array<UIAButton|XCUIElementTypeButton>]
27
+ def buttons(value = false)
28
+ return tags button_class unless value
29
+ elements = find_eles_by_predicate_include(class_name: button_class, value: value)
30
+ select_visible_elements elements
31
+ end
32
+
33
+ # Find the first UIAButton|XCUIElementTypeButton.
34
+ # @return [UIAButton|XCUIElementTypeButton]
35
+ def first_button
36
+ first_ele button_class
37
+ end
38
+
39
+ # TODO: add documentation regarding previous element.
40
+ # Previous UIAElement is differ from UIAButton|XCUIElementTypeButton. So, the results are different.
41
+ # Find the last UIAButton|XCUIElementTypeButton.
42
+ # @return [UIAButton|XCUIElementTypeButton]
43
+ def last_button
44
+ last_ele button_class
45
+ end
46
+
47
+ # Find the first UIAButton|XCUIElementTypeButton that exactly matches value.
48
+ # @param value [String] the value to match exactly
49
+ # @return [UIAButton|XCUIElementTypeButton]
50
+ def button_exact(value)
51
+ raise_error_if_no_element buttons_exact(value).first
52
+ end
53
+
54
+ # Find all UIAButtons|XCUIElementTypeButtons that exactly match value.
55
+ # @param value [String] the value to match exactly
56
+ # @return [Array<UIAButton|XCUIElementTypeButton>]
57
+ def buttons_exact(value)
58
+ elements = find_eles_by_predicate(class_name: button_class, value: value)
59
+ select_visible_elements elements
60
+ end
61
+ end # moudule button
62
+ end # module Xcuitest
63
+ end # module Ios
64
+ end # module Appium
@@ -0,0 +1,50 @@
1
+ module Appium
2
+ module Ios
3
+ module Xcuitest
4
+ module Element
5
+ # Find the first element containing value
6
+ # @param value [String] the value to search for
7
+ # @return [Element]
8
+ def find(value)
9
+ raise_error_if_no_element finds(value).first
10
+ end
11
+
12
+ # Find all elements containing value
13
+ # @param value [String] the value to search for
14
+ # @return [Array<Element>]
15
+ def finds(value)
16
+ elements = find_eles_by_predicate_include value: value
17
+ select_visible_elements elements
18
+ end
19
+
20
+ # Find the first element exactly matching value
21
+ # @param value [String] the value to search for
22
+ # @return [Element]
23
+ def find_exact(value)
24
+ raise_error_if_no_element finds_exact(value).first
25
+ end
26
+
27
+ # Find all elements exactly matching value
28
+ # @param value [String] the value to search for
29
+ # @return [Array<Element>]
30
+ def finds_exact(value)
31
+ elements = find_eles_by_predicate value: value
32
+ select_visible_elements elements
33
+ end
34
+
35
+ private
36
+
37
+ def raise_error_if_no_element(element)
38
+ error_message = 'An element could not be located on the page using the given search parameters.'
39
+ raise(::Selenium::WebDriver::Error::NoSuchElementError, error_message) if element.nil?
40
+ element
41
+ end
42
+
43
+ # Return visible elements.
44
+ def select_visible_elements(elements)
45
+ elements.select(&:displayed?)
46
+ end
47
+ end # module Element
48
+ end # module Xcuitest
49
+ end # module Ios
50
+ end # module Appium
@@ -0,0 +1,61 @@
1
+ # UIAStaticText|XCUIElementTypeStaticText methods
2
+ module Appium
3
+ module Ios
4
+ module Xcuitest
5
+ module Element
6
+ # XCUIElementTypeStaticText = 'XCUIElementTypeStaticText'.freeze
7
+
8
+ # @return [String] Class name for text
9
+ def static_text_class
10
+ ::Appium::Ios::XCUIElementTypeStaticText
11
+ end
12
+
13
+ # Find the first UIAStaticText|XCUIElementTypeStaticText that contains value or by index.
14
+ # @param value [String, Integer] the value to find.
15
+ # If int then the UIAStaticText|XCUIElementTypeStaticText at that index is returned.
16
+ # @return [UIAStaticText|XCUIElementTypeStaticText]
17
+ def text(value)
18
+ return ele_index static_text_class, value if value.is_a? Numeric
19
+ raise_error_if_no_element texts(value).first
20
+ end
21
+
22
+ # Find all UIAStaticTexts|XCUIElementTypeStaticTexts containing value.
23
+ # If value is omitted, all UIAStaticTexts|XCUIElementTypeStaticTexts are returned
24
+ # @param value [String] the value to search for
25
+ # @return [Array<UIAStaticText|XCUIElementTypeStaticText>]
26
+ def texts(value = false)
27
+ return tags static_text_class unless value
28
+ elements = find_eles_by_predicate_include(class_name: static_text_class, value: value)
29
+ select_visible_elements elements
30
+ end
31
+
32
+ # Find the first UIAStaticText|XCUIElementTypeStaticText.
33
+ # @return [UIAStaticText|XCUIElementTypeStaticText]
34
+ def first_text
35
+ first_ele static_text_class
36
+ end
37
+
38
+ # Find the last UIAStaticText|XCUIElementTypeStaticText.
39
+ # @return [UIAStaticText|XCUIElementTypeStaticText]
40
+ def last_text
41
+ last_ele static_text_class
42
+ end
43
+
44
+ # Find the first UIAStaticText|XCUIElementTypeStaticText that exactly matches value.
45
+ # @param value [String] the value to match exactly
46
+ # @return [UIAStaticText|XCUIElementTypeStaticText]
47
+ def text_exact(value)
48
+ raise_error_if_no_element texts_exact(value).first
49
+ end
50
+
51
+ # Find all UIAStaticTexts|XCUIElementTypeStaticTexts that exactly match value.
52
+ # @param value [String] the value to match exactly
53
+ # @return [Array<UIAStaticText|XCUIElementTypeStaticText>]
54
+ def texts_exact(value)
55
+ elements = find_eles_by_predicate(class_name: static_text_class, value: value)
56
+ select_visible_elements elements
57
+ end
58
+ end # module Text
59
+ end # module XCUITest
60
+ end # module Ios
61
+ end # module Appium
@@ -0,0 +1,94 @@
1
+ module Appium
2
+ module Ios
3
+ module Xcuitest
4
+ module Element
5
+ # XCUIElementTypeTextField = 'XCUIElementTypeTextField'.freeze
6
+ # XCUIElementTypeSecureTextField = 'XCUIElementTypeSecureTextField'.freeze
7
+
8
+ # @return [String] Class name for text field
9
+ def text_field_class
10
+ ::Appium::Ios::XCUIElementTypeTextField
11
+ end
12
+
13
+ # @return [String] Class name for secure text field
14
+ def secure_text_field_class
15
+ ::Appium::Ios::XCUIElementTypeSecureTextField
16
+ end
17
+
18
+ # Find the first TextField that contains value or by index.
19
+ # Note: Uses XPath
20
+ # @param value [String, Integer] the text to match exactly.
21
+ # If int then the TextField at that index is returned.
22
+ # @return [TextField]
23
+ def textfield(value)
24
+ if value.is_a? Numeric
25
+ index = value
26
+ raise "#{index} is not a valid index. Must be >= 1" if index <= 0
27
+ index -= 1 # eles_by_json and _textfields_with_predicate is 0 indexed.
28
+ result = _textfields_with_predicate[index]
29
+ raise _no_such_element if result.nil?
30
+ return result
31
+
32
+ end
33
+
34
+ raise_error_if_no_element textfields(value).first
35
+ end
36
+
37
+ # Find all TextFields containing value.
38
+ # If value is omitted, all TextFields are returned.
39
+ # @param value [String] the value to search for
40
+ # @return [Array<TextField>]
41
+ def textfields(value = false)
42
+ return tags_include(class_names: [text_field_class, secure_text_field_class]) unless value
43
+
44
+ elements = tags_include class_names: [text_field_class, secure_text_field_class], value: value
45
+ select_visible_elements elements
46
+ end
47
+
48
+ # Find the first TextField.
49
+ # @return [TextField]
50
+ def first_textfield
51
+ _textfield_with_predicate
52
+ end
53
+
54
+ # Find the last TextField.
55
+ # @return [TextField]
56
+ def last_textfield
57
+ result = _textfields_with_predicate.last
58
+ raise _no_such_element if result.nil?
59
+ result
60
+ end
61
+
62
+ # Find the first TextField that exactly matches value.
63
+ # @param value [String] the value to match exactly
64
+ # @return [TextField]
65
+ def textfield_exact(value)
66
+ raise_error_if_no_element textfields_exact(value).first
67
+ end
68
+
69
+ # Find all TextFields that exactly match value.
70
+ # @param value [String] the value to match exactly
71
+ # @return [Array<TextField>]
72
+ def textfields_exact(value)
73
+ elements = tags_exact class_names: [text_field_class, secure_text_field_class], value: value
74
+ select_visible_elements elements
75
+ end
76
+
77
+ private
78
+
79
+ # @private
80
+ # for XCUITest
81
+ def _textfield_with_predicate
82
+ raise_error_if_no_element _textfields_with_predicate.first
83
+ end
84
+
85
+ # @private
86
+ # for XCUITest
87
+ def _textfields_with_predicate
88
+ elements = tags_include(class_names: [text_field_class, secure_text_field_class])
89
+ select_visible_elements elements
90
+ end
91
+ end # module TextField
92
+ end # module XCUITest
93
+ end # module Ios
94
+ end # module Appium
@@ -9,12 +9,10 @@ module Appium
9
9
  # swipe direction: "down"
10
10
  # ```
11
11
  def swipe(direction:, element: nil)
12
- return unless %w(up down left right).include?(direction)
13
-
14
12
  args = { direction: direction }
15
13
  args[:element] = element.ref if element
16
14
 
17
- execute_script 'mobile: swipe', args
15
+ @driver.execute_script 'mobile: swipe', args
18
16
  end
19
17
 
20
18
  # @param [string] direction Either 'up', 'down', 'left' or 'right'.
@@ -37,7 +35,7 @@ module Appium
37
35
  args[:toVisible] = to_visible if to_visible
38
36
  args[:predicateString] = predicate_string if predicate_string
39
37
 
40
- execute_script 'mobile: scroll', args
38
+ @driver.execute_script 'mobile: scroll', args
41
39
  end
42
40
 
43
41
  # @param scale [scale] X tap coordinate of type float. Mandatory parameter
@@ -48,12 +46,10 @@ module Appium
48
46
  # pinch scale: 0.5, velocity: -1
49
47
  # ```
50
48
  def pinch(scale:, velocity: 1.0, element: nil)
51
- return unless automation_name_is_xcuitest?
52
-
53
49
  args = { scale: scale, velocity: velocity }
54
50
  args[:element] = element.ref if element
55
51
 
56
- execute_script 'mobile: pinch', args
52
+ @driver.execute_script 'mobile: pinch', args
57
53
  end
58
54
 
59
55
  # @param x [float] X Screen x tap coordinate of type float. Mandatory parameter only if element is not set
@@ -65,11 +61,10 @@ module Appium
65
61
  # double_tap element: find_element(:accessibility_id, "some item")
66
62
  # ```
67
63
  def double_tap(x: nil, y: nil, element: nil)
68
- return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
69
64
  return 'Set x, y or element' if (x.nil? || y.nil?) && element.nil?
70
65
 
71
66
  args = element.nil? ? { x: x, y: y } : { element: element.ref }
72
- execute_script 'mobile: doubleTap', args
67
+ @driver.execute_script 'mobile: doubleTap', args
73
68
  end
74
69
 
75
70
  # @param x [float] Screen x long tap coordinate of type float. Mandatory parameter only if element is not set
@@ -83,12 +78,11 @@ module Appium
83
78
  # touch_and_hold element: find_element(:accessibility_id, "some item")
84
79
  # ```
85
80
  def touch_and_hold(x: nil, y: nil, element: nil, duration: 1.0)
86
- return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
87
81
  return 'Set x, y or element' if (x.nil? || y.nil?) && element.nil?
88
82
 
89
83
  args = element.nil? ? { x: x, y: y } : { element: element.ref }
90
84
  args[:duration] = duration
91
- execute_script 'mobile: touchAndHold', args
85
+ @driver.execute_script 'mobile: touchAndHold', args
92
86
  end
93
87
 
94
88
  # @param [Element] :element Element to long tap on.
@@ -97,10 +91,8 @@ module Appium
97
91
  # two_finger_tap element: find_element(:accessibility_id, "some item")
98
92
  # ```
99
93
  def two_finger_tap(element:)
100
- return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
101
-
102
94
  args = { element: element.ref }
103
- execute_script 'mobile: twoFingerTap', args
95
+ @driver.execute_script 'mobile: twoFingerTap', args
104
96
  end
105
97
 
106
98
  # @param x [float] X tap coordinate of type float. Mandatory parameter
@@ -114,11 +106,9 @@ module Appium
114
106
  # tap x: 100, y: 100, element: find_element(:accessibility_id, "some item")
115
107
  # ```
116
108
  def tap(x:, y:, element: nil)
117
- return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
118
-
119
109
  args = { x: x, y: y }
120
110
  args[:element] = element.ref if element
121
- execute_script 'mobile: tap', args
111
+ @driver.execute_script 'mobile: tap', args
122
112
  end
123
113
 
124
114
  # rubocop:disable Metrics/ParameterLists
@@ -136,11 +126,9 @@ module Appium
136
126
  # drag_from_to_for_duration from_x: 100, from_y: 100, to_x: 150, to_y: 150
137
127
  # ```
138
128
  def drag_from_to_for_duration(from_x:, from_y:, to_x:, to_y:, duration: 1.0, element: nil)
139
- return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
140
-
141
129
  args = { fromX: from_x, fromY: from_y, toX: to_x, toY: to_y, duration: duration }
142
130
  args[:element] = element.ref if element
143
- execute_script 'mobile: dragFromToForDuration', args
131
+ @driver.execute_script 'mobile: dragFromToForDuration', args
144
132
  end
145
133
  # rubocop:enable Metrics/ParameterLists
146
134
 
@@ -155,12 +143,11 @@ module Appium
155
143
  # select_picker_wheel order: "next", element: find_element(:accessibility_id, "some picker")
156
144
  # ```
157
145
  def select_picker_wheel(element:, order:, offset: nil)
158
- return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
159
146
  return 'Set "next" or "previous" for :order' unless %w(next previous).include?(order)
160
147
 
161
148
  args = { element: element.ref, order: order }
162
149
  args[:offset] = offset if offset
163
- execute_script 'mobile: selectPickerWheelValue', args
150
+ @driver.execute_script 'mobile: selectPickerWheelValue', args
164
151
  end
165
152
 
166
153
  # @param action [String] The following actions are supported: accept, dismiss and getButtons. Mandatory parameter
@@ -178,7 +165,7 @@ module Appium
178
165
 
179
166
  args = { action: action }
180
167
  args[:button_label] if button_label
181
- execute_script 'mobile: alert', args
168
+ @driver.execute_script 'mobile: alert', args
182
169
  end
183
170
  end # module Gesture
184
171
  end # module Xcuitest