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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +43 -0
- data/Rakefile +1 -1
- data/appium_lib.gemspec +1 -1
- data/docs/android_docs.md +440 -1295
- data/docs/docs.md +10 -103
- data/docs/index_paths.md +2 -0
- data/docs/ios_docs.md +725 -1674
- data/docs/migration.md +17 -0
- data/lib/appium_lib.rb +1 -2
- data/lib/appium_lib/android/android.rb +20 -0
- data/lib/appium_lib/android/{helper.rb → common/helper.rb} +1 -1
- data/lib/appium_lib/android/uiautomator2.rb +5 -4
- data/lib/appium_lib/android/uiautomator2/bridge.rb +16 -0
- data/lib/appium_lib/appium.rb +201 -0
- data/lib/appium_lib/common/helper.rb +18 -20
- data/lib/appium_lib/common/log.rb +24 -0
- data/lib/appium_lib/common/multi_touch.rb +89 -0
- data/lib/appium_lib/common/touch_actions.rb +48 -0
- data/lib/appium_lib/common/wait.rb +10 -49
- data/lib/appium_lib/core/android.rb +4 -0
- data/lib/appium_lib/core/android/device.rb +142 -0
- data/lib/appium_lib/core/android/search_context.rb +17 -0
- data/lib/appium_lib/core/android/uiautomator1/bridge.rb +16 -0
- data/lib/appium_lib/core/android/uiautomator2/bridge.rb +16 -0
- data/lib/appium_lib/core/android_uiautomator2.rb +4 -0
- data/lib/appium_lib/core/common.rb +6 -0
- data/lib/appium_lib/core/common/base.rb +8 -0
- data/lib/appium_lib/core/common/base/bridge.rb +47 -0
- data/lib/appium_lib/core/common/base/capabilities.rb +16 -0
- data/lib/appium_lib/core/common/base/command.rb +10 -0
- data/lib/appium_lib/core/common/base/driver.rb +40 -0
- data/lib/appium_lib/core/common/base/http_default.rb +12 -0
- data/lib/appium_lib/core/common/base/search_context.rb +89 -0
- data/lib/appium_lib/core/common/base/wait.rb +56 -0
- data/lib/appium_lib/{common → core/common}/command.rb +20 -16
- data/lib/appium_lib/core/common/device.rb +470 -0
- data/lib/appium_lib/core/common/error.rb +13 -0
- data/lib/appium_lib/core/common/log.rb +30 -0
- data/lib/appium_lib/{logger.rb → core/common/logger.rb} +2 -0
- data/lib/appium_lib/core/core.rb +38 -0
- data/lib/appium_lib/core/device/multi_touch.rb +213 -0
- data/lib/appium_lib/core/device/touch_actions.rb +206 -0
- data/lib/appium_lib/core/driver.rb +274 -0
- data/lib/appium_lib/core/ios.rb +6 -0
- data/lib/appium_lib/core/ios/device.rb +44 -0
- data/lib/appium_lib/core/ios/search_context.rb +27 -0
- data/lib/appium_lib/core/ios/uiautomation/bridge.rb +17 -0
- data/lib/appium_lib/core/ios/uiautomation/patch.rb +20 -0
- data/lib/appium_lib/core/ios/xcuitest/bridge.rb +18 -0
- data/lib/appium_lib/{ios → core/ios}/xcuitest/device.rb +5 -5
- data/lib/appium_lib/{ios → core/ios}/xcuitest/search_context.rb +13 -9
- data/lib/appium_lib/core/ios_xcuitest.rb +7 -0
- data/lib/appium_lib/core/patch.rb +56 -0
- data/lib/appium_lib/driver.rb +174 -446
- data/lib/appium_lib/ios/{errors.rb → common/errors.rb} +0 -0
- data/lib/appium_lib/ios/{helper.rb → common/helper.rb} +9 -110
- data/lib/appium_lib/ios/ios.rb +20 -0
- data/lib/appium_lib/ios/xcuitest.rb +1 -3
- data/lib/appium_lib/ios/xcuitest/bridge.rb +19 -0
- data/lib/appium_lib/ios/xcuitest/command.rb +4 -1
- data/lib/appium_lib/ios/xcuitest/{gestures.rb → command/gestures.rb} +1 -1
- data/lib/appium_lib/ios/xcuitest/element.rb +1 -18
- data/lib/appium_lib/ios/xcuitest/helper.rb +0 -6
- data/lib/appium_lib/sauce_labs.rb +29 -0
- data/lib/appium_lib/version.rb +5 -0
- data/release_notes.md +8 -0
- metadata +50 -25
- data/lib/appium_lib/android/client_xpath.rb +0 -51
- data/lib/appium_lib/android/device.rb +0 -39
- data/lib/appium_lib/android/mobile_methods.rb +0 -15
- data/lib/appium_lib/android/patch.rb +0 -16
- data/lib/appium_lib/capabilities.rb +0 -13
- data/lib/appium_lib/common/element/window.rb +0 -10
- data/lib/appium_lib/common/error.rb +0 -8
- data/lib/appium_lib/common/patch.rb +0 -190
- data/lib/appium_lib/common/search_context.rb +0 -10
- data/lib/appium_lib/common/version.rb +0 -5
- data/lib/appium_lib/device/device.rb +0 -611
- data/lib/appium_lib/device/multi_touch.rb +0 -225
- data/lib/appium_lib/device/touch_actions.rb +0 -230
- data/lib/appium_lib/ios/mobile_methods.rb +0 -25
- data/lib/appium_lib/ios/patch.rb +0 -22
|
@@ -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
|
|
@@ -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
|