appium_lib 9.6.1 → 9.7.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/CHANGELOG.md +43 -0
  4. data/Rakefile +1 -1
  5. data/appium_lib.gemspec +1 -1
  6. data/docs/android_docs.md +440 -1295
  7. data/docs/docs.md +10 -103
  8. data/docs/index_paths.md +2 -0
  9. data/docs/ios_docs.md +725 -1674
  10. data/docs/migration.md +17 -0
  11. data/lib/appium_lib.rb +1 -2
  12. data/lib/appium_lib/android/android.rb +20 -0
  13. data/lib/appium_lib/android/{helper.rb → common/helper.rb} +1 -1
  14. data/lib/appium_lib/android/uiautomator2.rb +5 -4
  15. data/lib/appium_lib/android/uiautomator2/bridge.rb +16 -0
  16. data/lib/appium_lib/appium.rb +201 -0
  17. data/lib/appium_lib/common/helper.rb +18 -20
  18. data/lib/appium_lib/common/log.rb +24 -0
  19. data/lib/appium_lib/common/multi_touch.rb +89 -0
  20. data/lib/appium_lib/common/touch_actions.rb +48 -0
  21. data/lib/appium_lib/common/wait.rb +10 -49
  22. data/lib/appium_lib/core/android.rb +4 -0
  23. data/lib/appium_lib/core/android/device.rb +142 -0
  24. data/lib/appium_lib/core/android/search_context.rb +17 -0
  25. data/lib/appium_lib/core/android/uiautomator1/bridge.rb +16 -0
  26. data/lib/appium_lib/core/android/uiautomator2/bridge.rb +16 -0
  27. data/lib/appium_lib/core/android_uiautomator2.rb +4 -0
  28. data/lib/appium_lib/core/common.rb +6 -0
  29. data/lib/appium_lib/core/common/base.rb +8 -0
  30. data/lib/appium_lib/core/common/base/bridge.rb +47 -0
  31. data/lib/appium_lib/core/common/base/capabilities.rb +16 -0
  32. data/lib/appium_lib/core/common/base/command.rb +10 -0
  33. data/lib/appium_lib/core/common/base/driver.rb +40 -0
  34. data/lib/appium_lib/core/common/base/http_default.rb +12 -0
  35. data/lib/appium_lib/core/common/base/search_context.rb +89 -0
  36. data/lib/appium_lib/core/common/base/wait.rb +56 -0
  37. data/lib/appium_lib/{common → core/common}/command.rb +20 -16
  38. data/lib/appium_lib/core/common/device.rb +470 -0
  39. data/lib/appium_lib/core/common/error.rb +13 -0
  40. data/lib/appium_lib/core/common/log.rb +30 -0
  41. data/lib/appium_lib/{logger.rb → core/common/logger.rb} +2 -0
  42. data/lib/appium_lib/core/core.rb +38 -0
  43. data/lib/appium_lib/core/device/multi_touch.rb +213 -0
  44. data/lib/appium_lib/core/device/touch_actions.rb +206 -0
  45. data/lib/appium_lib/core/driver.rb +274 -0
  46. data/lib/appium_lib/core/ios.rb +6 -0
  47. data/lib/appium_lib/core/ios/device.rb +44 -0
  48. data/lib/appium_lib/core/ios/search_context.rb +27 -0
  49. data/lib/appium_lib/core/ios/uiautomation/bridge.rb +17 -0
  50. data/lib/appium_lib/core/ios/uiautomation/patch.rb +20 -0
  51. data/lib/appium_lib/core/ios/xcuitest/bridge.rb +18 -0
  52. data/lib/appium_lib/{ios → core/ios}/xcuitest/device.rb +5 -5
  53. data/lib/appium_lib/{ios → core/ios}/xcuitest/search_context.rb +13 -9
  54. data/lib/appium_lib/core/ios_xcuitest.rb +7 -0
  55. data/lib/appium_lib/core/patch.rb +56 -0
  56. data/lib/appium_lib/driver.rb +174 -446
  57. data/lib/appium_lib/ios/{errors.rb → common/errors.rb} +0 -0
  58. data/lib/appium_lib/ios/{helper.rb → common/helper.rb} +9 -110
  59. data/lib/appium_lib/ios/ios.rb +20 -0
  60. data/lib/appium_lib/ios/xcuitest.rb +1 -3
  61. data/lib/appium_lib/ios/xcuitest/bridge.rb +19 -0
  62. data/lib/appium_lib/ios/xcuitest/command.rb +4 -1
  63. data/lib/appium_lib/ios/xcuitest/{gestures.rb → command/gestures.rb} +1 -1
  64. data/lib/appium_lib/ios/xcuitest/element.rb +1 -18
  65. data/lib/appium_lib/ios/xcuitest/helper.rb +0 -6
  66. data/lib/appium_lib/sauce_labs.rb +29 -0
  67. data/lib/appium_lib/version.rb +5 -0
  68. data/release_notes.md +8 -0
  69. metadata +50 -25
  70. data/lib/appium_lib/android/client_xpath.rb +0 -51
  71. data/lib/appium_lib/android/device.rb +0 -39
  72. data/lib/appium_lib/android/mobile_methods.rb +0 -15
  73. data/lib/appium_lib/android/patch.rb +0 -16
  74. data/lib/appium_lib/capabilities.rb +0 -13
  75. data/lib/appium_lib/common/element/window.rb +0 -10
  76. data/lib/appium_lib/common/error.rb +0 -8
  77. data/lib/appium_lib/common/patch.rb +0 -190
  78. data/lib/appium_lib/common/search_context.rb +0 -10
  79. data/lib/appium_lib/common/version.rb +0 -5
  80. data/lib/appium_lib/device/device.rb +0 -611
  81. data/lib/appium_lib/device/multi_touch.rb +0 -225
  82. data/lib/appium_lib/device/touch_actions.rb +0 -230
  83. data/lib/appium_lib/ios/mobile_methods.rb +0 -25
  84. data/lib/appium_lib/ios/patch.rb +0 -22
@@ -0,0 +1,13 @@
1
+ module Appium
2
+ module Core
3
+ module Error
4
+ class CoreError < StandardError; end
5
+
6
+ class NotSupportedAppiumServer < CoreError; end
7
+ class NoSuchElementError < CoreError; end
8
+
9
+ # Server side error
10
+ class ServerError; end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ module Appium
2
+ module Core
3
+ class Logs
4
+ def initialize(bridge)
5
+ @bridge = bridge
6
+ end
7
+
8
+ # @param [String|Hash] type You can get particular type's logs.
9
+ # @return [[Selenium::WebDriver::LogEntry]] A list of logs data.
10
+ #
11
+ # @example
12
+ # Appium::Core::Logs.new(driver).get("syslog") #=> [[Selenium::WebDriver::LogEntry]]
13
+ # Appium::Core::Logs.new(driver).get(:syslog) #=> [[Selenium::WebDriver::LogEntry]]
14
+ #
15
+ def get(type)
16
+ @bridge.get type
17
+ end
18
+
19
+ # Get a list of available log types
20
+ #
21
+ # @return [[Hash]] A list of available log types.
22
+ # @example
23
+ # Appium::Core::Logs.new(driver).available_types #=> [:syslog, :crashlog, :performance]
24
+ #
25
+ def available_types
26
+ @bridge.available_types
27
+ end
28
+ end
29
+ end # module Core
30
+ end # module Appium
@@ -1,4 +1,6 @@
1
+ require 'forwardable'
1
2
  require 'logger'
3
+ require 'ap'
2
4
 
3
5
  module Appium
4
6
  module Logger
@@ -0,0 +1,38 @@
1
+ require 'selenium-webdriver'
2
+
3
+ require_relative 'common'
4
+ require_relative 'patch'
5
+ require_relative 'driver'
6
+
7
+ # for multi touch related methods
8
+ require_relative 'device/touch_actions'
9
+ require_relative 'device/multi_touch'
10
+
11
+ require_relative 'android'
12
+ require_relative 'android_uiautomator2'
13
+
14
+ require_relative 'ios'
15
+ require_relative 'ios_xcuitest'
16
+
17
+ module Appium
18
+ # convert all keys (including nested) to symbols
19
+ #
20
+ # based on deep_symbolize_keys & deep_transform_keys from rails
21
+ # https://github.com/rails/docrails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/activesupport/lib/active_support/core_ext/hash/keys.rb#L84
22
+ # @param [Hash] hash Hash value to make symbolise
23
+ def self.symbolize_keys(hash)
24
+ raise 'symbolize_keys requires a hash' unless hash.is_a? Hash
25
+ result = {}
26
+ hash.each do |key, value|
27
+ key = key.to_sym rescue key # rubocop:disable Style/RescueModifier
28
+ result[key] = value.is_a?(Hash) ? symbolize_keys(value) : value
29
+ end
30
+ result
31
+ end
32
+
33
+ module Core
34
+ def self.for(*args)
35
+ Core::Driver.for(*args)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,213 @@
1
+ module Appium
2
+ module Core
3
+ # MultiTouch actions allow for multiple touches to happen at the same time,
4
+ # for instance, to simulate multiple finger swipes.
5
+ #
6
+ # Create a series of touch actions by themselves (without a `prepare()`), then
7
+ # add to a new MultiTouch action. When ready, call `prepare()` and all
8
+ # actions will be executed simultaneously.
9
+ #
10
+ # ```ruby
11
+ # action_1 = TouchAction.new.press(x: 45, y: 100).wait(5).release
12
+ # action_2 = TouchAction.new.tap(element: el, x: 50, y:5, count: 3)
13
+ #
14
+ # driver = Appium::Driver.new(opts, false).start_driver
15
+ # multi_touch_action = MultiTouch.new(driver)
16
+ # multi_touch_action.add action_1
17
+ # multi_touch_action.add action_2
18
+ # multi_touch_action.perform
19
+ # ```
20
+
21
+ class MultiTouch
22
+ class << self
23
+ # Convenience method for pinching the screen.
24
+ # Places two fingers at the edges of the screen and brings them together.
25
+ # @param percentage (int) The percent size by which to shrink the screen when pinched.
26
+ # @param auto_perform (boolean) Whether to perform the action immediately (default true)
27
+ # @param driver (Driver) Set a driver to conduct the action. DEfault is global driver($driver)
28
+ #
29
+ # ```ruby
30
+ # driver = Appium::Driver.new(opts, false).start_driver
31
+ # pinch percentage: 75, driver: driver #=> Pinch the screen from the top right and bottom left corners
32
+ # ```
33
+ #
34
+ # Without auto_perform
35
+ #
36
+ # ```ruby
37
+ # driver = Appium::Driver.new(opts, false).start_driver
38
+ # action = pinch percentage: 75, auto_perform: false, driver: driver
39
+ # #=> Pinch the screen from the top right and bottom left corners
40
+ # action.perform #=> to 25% of its size.
41
+ # ```
42
+ def pinch(percentage: 25, auto_perform: true, driver:)
43
+ raise ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
44
+
45
+ rate = Float(percentage) / 100
46
+ pinch = MultiTouch.new(driver)
47
+
48
+ # TODO: Don't use driver automation handler here.
49
+ if pinch.driver.automation_name_is_xcuitest?
50
+ top, bottom = pinch_for_xcuitest(rate, pinch.driver)
51
+ elsif pinch.driver.device_is_android?
52
+ top, bottom = pinch_android(rate, pinch.driver)
53
+ else
54
+ top, bottom = pinch_ios(rate, pinch.driver)
55
+ end
56
+
57
+ pinch.add top
58
+ pinch.add bottom
59
+ return pinch unless auto_perform
60
+ pinch.perform
61
+ end
62
+
63
+ # Convenience method for zooming the screen.
64
+ # Places two fingers at the edges of the screen and brings them together.
65
+ # @param percentage (int) The percent size by which to shrink the screen when pinched.
66
+ # @param auto_perform (boolean) Whether to perform the action immediately (default true)
67
+ # @param driver (Driver) Set a driver to conduct the action. DEfault is global driver($driver)
68
+ #
69
+ # ```ruby
70
+ # driver = Appium::Driver.new(opts, false).start_driver
71
+ # action = zoom percentage: 200, driver: driver #=> Zoom in the screen from the center until it doubles in size.
72
+ # ```
73
+ #
74
+ # Without auto_perform
75
+ #
76
+ # ```ruby
77
+ # driver = Appium::Driver.new(opts, false).start_driver
78
+ # action = zoom percentage: 200, auto_perform: false, driver: driver
79
+ # #=> Zoom in the screen from the center until it doubles in size.
80
+ # action.perform #=> to 25% of its size.
81
+ # ```
82
+ def zoom(percentage: 200, auto_perform: true, driver:)
83
+ raise ArgumentError("Can't zoom to smaller then screen size.") if percentage < 100
84
+
85
+ rate = 100 / Float(percentage)
86
+ zoom = MultiTouch.new(driver)
87
+
88
+ # TODO: Don't use driver automation handler here.
89
+ if zoom.driver.automation_name_is_xcuitest?
90
+ top, bottom = zoom_for_xcuitest(rate, zoom.driver)
91
+ elsif zoom.driver.device_is_android?
92
+ top, bottom = zoom_android(rate, zoom.driver)
93
+ else
94
+ top, bottom = zoom_ios(rate, zoom.driver)
95
+ end
96
+
97
+ zoom.add top
98
+ zoom.add bottom
99
+ return zoom unless auto_perform
100
+ zoom.perform
101
+ end
102
+
103
+ private
104
+
105
+ def pinch_for_xcuitest(rate, driver)
106
+ height = 100
107
+
108
+ ele = driver.find_element :class, 'XCUIElementTypeApplication'
109
+ top = ::Appium::Core::TouchAction.new(driver)
110
+ top.swipe({ start_x: 0.5, start_y: 0.0,
111
+ offset_x: 0.0, offset_y: (1 - rate) * height }, ele)
112
+
113
+ bottom = ::Appium::Core::TouchAction.new(driver)
114
+ bottom.swipe({ start_x: 0.5, start_y: 1.0,
115
+ offset_x: 0.0, offset_y: rate * height }, ele)
116
+
117
+ [top, bottom]
118
+ end
119
+
120
+ def pinch_android(rate, driver)
121
+ height = 100
122
+
123
+ top = ::Appium::Core::TouchAction.new(driver)
124
+ top.swipe start_x: 0.5, start_y: 1.0 * height,
125
+ end_x: 0.5, end_y: rate * height, duration: 1_000
126
+
127
+ bottom = ::Appium::Core::TouchAction.new(driver)
128
+ bottom.swipe start_x: 0.5, start_y: 0.0,
129
+ end_x: 0.5, end_y: (1 - rate) * height, duration: 1_000
130
+
131
+ [top, bottom]
132
+ end
133
+
134
+ def pinch_ios(rate, driver)
135
+ height = 100
136
+
137
+ top = ::Appium::Core::TouchAction.new(driver)
138
+ top.swipe start_x: 0.5, start_y: 0.0,
139
+ offset_x: 0.0, offset_y: (1 - rate) * height, duration: 1_000
140
+
141
+ bottom = ::Appium::Core::TouchAction.new(driver)
142
+ bottom.swipe start_x: 0.5, start_y: 1.0,
143
+ offset_x: 0.0, offset_y: rate * height, duration: 1_000
144
+
145
+ [top, bottom]
146
+ end
147
+
148
+ def zoom_for_xcuitest(rate, driver)
149
+ height = 100
150
+
151
+ ele = driver.find_element :class, 'XCUIElementTypeApplication'
152
+ top = ::Appium::Core::TouchAction.new(driver)
153
+ top.swipe({ start_x: 0.5, start_y: (1 - rate) * height,
154
+ offset_x: 0.0, offset_y: - (1 - rate) * height }, ele)
155
+
156
+ bottom = ::Appium::Core::TouchAction.new(driver)
157
+ bottom.swipe({ start_x: 0.5, start_y: rate * height,
158
+ offset_x: 0.0, offset_y: (1 - rate) * height }, ele)
159
+
160
+ [top, bottom]
161
+ end
162
+
163
+ def zoom_android(rate, driver)
164
+ height = 100
165
+
166
+ top = ::Appium::Core::TouchAction.new(driver)
167
+ top.swipe start_x: 0.5, start_y: (1.0 - rate) * height,
168
+ end_x: 0.5, end_y: 0.0, duration: 1_000
169
+
170
+ bottom = ::Appium::Core::TouchAction.new(driver)
171
+ bottom.swipe start_x: 0.5, start_y: rate * height,
172
+ end_x: 0.5, end_y: 1.0 * height, duration: 1_000
173
+
174
+ [top, bottom]
175
+ end
176
+
177
+ def zoom_ios(rate, driver)
178
+ height = 100
179
+
180
+ top = ::Appium::Core::TouchAction.new(driver)
181
+ top.swipe start_x: 0.5, start_y: (1 - rate) * height,
182
+ offset_x: 0.0, offset_y: - (1 - rate) * height, duration: 1_000
183
+
184
+ bottom = ::Appium::Core::TouchAction.new(driver)
185
+ bottom.swipe start_x: 0.5, start_y: rate * height,
186
+ offset_x: 0.0, offset_y: (1 - rate) * height, duration: 1_000
187
+
188
+ [top, bottom]
189
+ end
190
+ end # self
191
+
192
+ attr_reader :actions, :driver
193
+
194
+ # Create a new multi-action with Driver
195
+ def initialize(driver)
196
+ @actions = []
197
+ @driver = driver
198
+ end
199
+
200
+ # Add a touch_action to be performed
201
+ # @param chain (TouchAction) The action to add to the chain
202
+ def add(chain)
203
+ @actions << chain.actions
204
+ end
205
+
206
+ # Ask Appium to perform the actions
207
+ def perform
208
+ @driver.multi_touch @actions
209
+ @actions.clear
210
+ end
211
+ end # class MultiTouch
212
+ end # module Core
213
+ end # module Appium
@@ -0,0 +1,206 @@
1
+ module Appium
2
+ # Perform a series of gestures, one after another. Gestures are chained
3
+ # together and only performed when `perform()` is called. Default is conducted by global driver.
4
+ #
5
+ # Each method returns the object itself, so calls can be chained.
6
+ #
7
+ # ```ruby
8
+ # action = TouchAction(@driver).new.press(x: 45, y: 100).wait(5).release
9
+ # action.perform
10
+ # action = TouchAction.new.swipe(....)
11
+ # action.perform
12
+ # ```
13
+ module Core
14
+ class TouchAction
15
+ ACTIONS = [:move_to, :long_press, :double_tap, :two_finger_tap, :press, :release, :tap, :wait, :perform].freeze
16
+ COMPLEX_ACTIONS = [:swipe].freeze
17
+
18
+ attr_reader :actions, :driver
19
+
20
+ def initialize(driver)
21
+ @actions = []
22
+ @driver = driver
23
+ end
24
+
25
+ # Move to the given co-ordinates.
26
+ #
27
+ # `move_to`'s `x` and `y` have two case. One is working as coordinate, the other is working as offset.
28
+ #
29
+ # @option opts [integer] :x x co-ordinate to move to if element isn't set. Works as an offset if x is set with Element.
30
+ # @option opts [integer] :y y co-ordinate to move to if element isn't set. Works as an offset if y is set with Element.
31
+ # @option opts [WebDriver::Element] Element to scope this move within.
32
+ def move_to(opts)
33
+ opts = args_with_ele_ref(opts)
34
+ chain_method(:moveTo, opts)
35
+ end
36
+
37
+ # Press down for a specific duration.
38
+ # Alternatively, you can use `press(...).wait(...).release()` instead of `long_press` if duration doesn't work well.
39
+ # https://github.com/appium/ruby_lib/issues/231#issuecomment-269895512
40
+ # e.g. Appium::TouchAction.new.press(x: 280, y: 530).wait(2000).release.perform
41
+ #
42
+ # @option element [WebDriver::Element] the element to press.
43
+ # @option x [integer] x co-ordinate to press on.
44
+ # @option y [integer] y co-ordinate to press on.
45
+ # @option duration [integer] Number of milliseconds to press.
46
+ def long_press(opts)
47
+ args = opts.select { |k, _v| [:element, :x, :y, :duration].include? k }
48
+ args = args_with_ele_ref(args)
49
+ chain_method(:longPress, args) # longPress is what the appium server expects
50
+ end
51
+
52
+ # Press a finger onto the screen. Finger will stay down until you call
53
+ # `release`.
54
+ #
55
+ # @option opts [WebDriver::Element] :element (Optional) Element to press within.
56
+ # @option opts [integer] :x x co-ordinate to press on
57
+ # @option opts [integer] :y y co-ordinate to press on
58
+ def press(opts)
59
+ args = opts.select { |k, _v| [:element, :x, :y].include? k }
60
+ args = args_with_ele_ref(args)
61
+ chain_method(:press, args)
62
+ end
63
+
64
+ # Remove a finger from the screen.
65
+ #
66
+ # @option opts [WebDriver::Element] :element (Optional) Element to release from.
67
+ # @option opts [integer] :x x co-ordinate to release from
68
+ # @option opts [integer] :y y co-ordinate to release from
69
+ def release(opts = nil)
70
+ args = args_with_ele_ref(opts) if opts
71
+ chain_method(:release, args)
72
+ end
73
+
74
+ # Touch a point on the screen.
75
+ # Alternatively, you can use `press(...).release.perform` instead of `tap(...).perform`.
76
+ #
77
+ # @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
78
+ # @option opts [integer] :x x co-ordinate to tap
79
+ # @option opts [integer] :y y co-ordinate to tap
80
+ # @option opts [integer] :fingers how many fingers to tap with (Default 1)
81
+ def tap(opts)
82
+ opts[:count] = opts.delete(:fingers) if opts[:fingers]
83
+ opts[:count] ||= 1
84
+ args = args_with_ele_ref opts
85
+ chain_method(:tap, args)
86
+ end
87
+
88
+ # Double tap an element on the screen
89
+ #
90
+ # @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
91
+ # @option opts [integer] :x x co-ordinate to tap
92
+ # @option opts [integer] :y y co-ordinate to tap
93
+
94
+ def double_tap(opts)
95
+ args = opts.select { |k, _v| [:element, :x, :y].include? k }
96
+ args = args_with_ele_ref(args)
97
+ chain_method(:doubleTap, args) # doubleTap is what the appium server expects
98
+ end
99
+
100
+ # Two finger tap an element on the screen
101
+ #
102
+ # @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
103
+ # @option opts [integer] :x x co-ordinate to tap
104
+ # @option opts [integer] :y y co-ordinate to tap
105
+ def two_finger_tap(opts)
106
+ args = opts.select { |k, _v| [:element, :x, :y].include? k }
107
+ args = args_with_ele_ref(args)
108
+ chain_method(:twoFingerTap, args) # twoFingerTap is what the appium server expects
109
+ end
110
+
111
+ # Pause for a number of milliseconds before the next action
112
+ # @param milliseconds [integer] Number of milliseconds to pause for
113
+ def wait(milliseconds)
114
+ args = { ms: milliseconds }
115
+ chain_method(:wait, args)
116
+ end
117
+
118
+ # Convenience method to peform a swipe.
119
+ #
120
+ # Note that iOS 7 simulators have broken swipe.
121
+ #
122
+ # For iOS: Use `offset_x` and `offset_y` to define the end point.
123
+ #
124
+ # For Android: Use `end_x` and `end_y` to define the end point.
125
+ #
126
+ # If you'd like more details, please read tests and its log samples in
127
+ # `ios_tests/lib/ios/specs/device/touch_actions.rb` and `ios_tests/lib/ios/specs/device/touch_actions.rb`
128
+ #
129
+ # @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
130
+ # @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
131
+ # @option opts [int] :offset_x For iOS. Offset, on the x axis. Default 0.
132
+ # @option opts [int] :offset_y For iOS. Offset, on the y axis. Default 0.
133
+ # @option opts [int] :end_x For Android. Where to end swiping, on the x axis. Default 0.
134
+ # @option opts [int] :end_y For Android. Where to end swiping, on the y axis. Default 0.
135
+ # @option opts [int] :duration How long the actual swipe takes to complete in milliseconds. Default 200.
136
+ def swipe(opts, ele = nil)
137
+ start_x = opts.fetch :start_x, 0
138
+ start_y = opts.fetch :start_y, 0
139
+ offset_x = opts.fetch :offset_x, nil
140
+ offset_y = opts.fetch :offset_y, nil
141
+ end_x = opts.fetch :end_x, nil
142
+ end_y = opts.fetch :end_y, nil
143
+ duration = opts.fetch :duration, 200
144
+
145
+ coordinates = swipe_coordinates(end_x: end_x, end_y: end_y, offset_x: offset_x, offset_y: offset_y)
146
+
147
+ if ele # pinch/zoom for XCUITest
148
+ press x: start_x, y: start_y, element: ele
149
+ move_to x: coordinates[:offset_x], y: coordinates[:offset_y], element: ele
150
+ else
151
+ press x: start_x, y: start_y
152
+ wait(duration) if duration
153
+ move_to x: coordinates[:offset_x], y: coordinates[:offset_y]
154
+ end
155
+ release
156
+
157
+ self
158
+ end
159
+
160
+ # Ask the driver to perform all actions in this action chain.
161
+ def perform
162
+ @driver.touch_actions @actions
163
+ @actions.clear
164
+ self
165
+ end
166
+
167
+ # Does nothing, currently.
168
+ def cancel
169
+ @actions << { action: cancel }
170
+ @driver.touch_actions @actions
171
+ self
172
+ end
173
+
174
+ # Visible for testing
175
+ # @private
176
+ def swipe_coordinates(end_x: nil, end_y: nil, offset_x: nil, offset_y: nil)
177
+ if @driver.device_is_android?
178
+ puts 'end_x and end_y are used for Android. Not offset_x and offset_y.' if end_x.nil? || end_y.nil?
179
+ end_x ||= 0
180
+ end_y ||= 0
181
+ return { offset_x: end_x, offset_y: end_y }
182
+ elsif offset_x.nil? || offset_y.nil?
183
+ puts 'offset_x and offset_y are used for iOS. Not end_x and end_y point.'
184
+ end
185
+
186
+ offset_x ||= 0
187
+ offset_y ||= 0
188
+
189
+ { offset_x: offset_x, offset_y: offset_y }
190
+ end
191
+
192
+ private
193
+
194
+ def chain_method(method, args = nil)
195
+ action = args ? { action: method, options: args } : { action: method }
196
+ @actions << action
197
+ self
198
+ end
199
+
200
+ def args_with_ele_ref(args)
201
+ args[:element] = args[:element].ref if args.key? :element
202
+ args
203
+ end
204
+ end # class TouchAction
205
+ end # module Core
206
+ end # module Appium