Skip to content

Commit a5ddd4a

Browse files
authored
fix swipe, pinch, zoom (#466)
* fix swipe * fix pinch and zoom * add documentation in move_to * rename end_* to offset_* * add documentations and log sample in tests * fix pinch and zoom to fit offset and end
1 parent a1c2e9e commit a5ddd4a

File tree

6 files changed

+232
-51
lines changed

6 files changed

+232
-51
lines changed

android_tests/lib/android/specs/common/device_touchaction.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
end
1212

1313
t 'swipe' do
14-
wait { Appium::TouchAction.new.swipe(start_x: 0.75, start_y: 0.25, delta_x: 0.75, delta_y: 0.5, duration: 1.5).perform }
14+
wait { Appium::TouchAction.new.swipe(start_x: 0.75, start_y: 0.25, end_x: 0.75, end_y: 50).perform }
1515
wait { !exists { text_exact 'NFC' } }
1616
wait { text_exact 'Bouncing Balls' }
1717
back

android_tests/lib/android/specs/device/touch_actions.rb

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,52 @@
1+
# rake android[device/touch_actions]
12
describe 'device/touch_actions' do
23
def swipe_till_text_visible(seen_text)
34
start_x = window_size[:width] / 2
45
start_y = window_size[:height] / 2
56
wait(60) do
6-
swipe start_x: start_x, start_y: start_y, delta_x: start_x, delta_y: start_y - 100
7+
# rubocop:disable Metrics/LineLength
8+
# Example for Android's log
9+
# [0] pry(#<device/touch_actions>)> swipe start_x: start_x, start_y: start_y, end_x: start_y, end_y: start_y - 100
10+
# [HTTP] --> POST /wd/hub/session/9baa18bf-a19b-4654-be4a-d0db0334bbc0/touch/perform {"actions":[{"action":"press","options":{"x":600,"y":919}},{"action":"wait","options":{"ms":200}},{"action":"moveTo","options":{"x":600,"y":819}},{"action":"release"}]}
11+
# [debug] [MJSONWP] Calling AppiumDriver.performTouch() with args: [[{"action":"press","options":{"x":600,"y":919}},{"action":"wait","options":{"ms":200}},{"action":"moveTo","options":{"x":600,"y":819}},{"action":"release"}],"9baa18bf-a19b-4654-be4a-d0db0334bbc0"]
12+
# [debug] [AndroidBootstrap] Sending command to android: {"cmd":"action","action":"swipe","params":{"startX":600,"startY":919,"endX":600,"endY":819,"steps":6}}
13+
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got data from client: {"cmd":"action","action":"swipe","params":{"startX":600,"startY":919,"endX":600,"endY":819,"steps":6}}
14+
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command of type ACTION
15+
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: swipe
16+
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Display bounds: [0,0][1200,1838]
17+
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Display bounds: [0,0][1200,1838]
18+
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Swiping from [x=600.0, y=919.0] to [x=600.0, y=819.0] with steps: 6
19+
# [debug] [AndroidBootstrap] Received command result from bootstrap
20+
# rubocop:enable Metrics/LineLength
21+
swipe start_x: start_x, start_y: start_y, end_x: start_x, end_y: start_y - 100
722
text(seen_text).displayed?
823
end
924
end
1025

26+
t 'swipe_coordinates_end_x_end_y' do
27+
s = Appium::TouchAction.new.swipe_coordinates(end_x: 1, end_y: 2)
28+
s[:offset_x].must_equal 1
29+
s[:offset_y].must_equal 2
30+
end
31+
32+
t 'swipe_coordinates_end_x' do
33+
s = Appium::TouchAction.new.swipe_coordinates(end_x: 1)
34+
s[:offset_x].must_equal 1
35+
s[:offset_y].must_equal 0
36+
end
37+
38+
t 'swipe_coordinates_end_y' do
39+
s = Appium::TouchAction.new.swipe_coordinates(end_y: 1)
40+
s[:offset_x].must_equal 0
41+
s[:offset_y].must_equal 1
42+
end
43+
44+
t 'swipe_coordinates_offset' do
45+
s = Appium::TouchAction.new.swipe_coordinates
46+
s[:offset_x].must_equal 0
47+
s[:offset_y].must_equal 0
48+
end
49+
1150
t 'swipe_default_duration' do
1251
swipe_till_text_visible('views')
1352
text('views').click

ios_tests/lib/ios/specs/device/device.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def go_back
8181
end
8282

8383
t 'swipe' do
84-
swipe start_x: 75, start_y: 500, delta_x: 0, delta_y: -500, duration: 800
84+
swipe start_x: 75, start_y: 500, offset_x: 75, offset_y: 0, duration: 800
8585
end
8686

8787
t 'pull_file' do

ios_tests/lib/ios/specs/device/touch_actions.rb

+53-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,62 @@ def after_last
1616
size = picker.size.to_h
1717
start_x = loc[:x] + size[:width] / 2
1818
start_y = loc[:y] + size[:height] / 2
19-
swipe start_x: start_x, start_y: start_y, delta_x: 0, delta_y: - 50
19+
# rubocop:disable Metrics/LineLength
20+
# Example for iOS's log with XCUITest
21+
# [0] pry(#<device/touch_actions>)> swipe start_x: start_x, start_y: start_y, offset_x: 0, offset_y: - 50
22+
# [debug] [WebDriverAgent] Sim: Jan 14 16:35:05 rrcs-172-254-99-35 CoreSimulatorBridge[65868]: KEYMAP: Chose mode=en_US@hw=Automatic;sw=QWERTY from match=en_US@hw=Automatic;sw=QWERTY from language=en
23+
# [HTTP] --> POST /wd/hub/session/cd65fa75-cbd7-4348-a7d6-60cbde28db47/touch/perform {"actions":[{"action":"press","options":{"x":187,"y":201}},{"action":"wait","options":{"ms":200}},{"action":"moveTo","options":{"x":0,"y":-50}},{"action":"release"}]}
24+
# [debug] [MJSONWP] Calling AppiumDriver.performTouch() with args: [[{"action":"press","options":{"x":187,"y":201}},{"action":"wait","options":{"ms":200}},{"action":"moveTo","options":{"x":0,"y":-50}},{"action":"release"}],"cd65fa75-cbd7-4348-a7d6-60cbde28db47"]
25+
# [debug] [XCUITest] Executing command 'performTouch'
26+
# [debug] [XCUITest] Received the following touch action: press-wait-moveTo-release
27+
# [debug] [JSONWP Proxy] Proxying [POST /uiaTarget/0/dragfromtoforduration] to [POST http://localhost:8100/session/0D2991FB-A997-4D6C-94FC-D65CC252EC4B/uiaTarget/0/dragfromtoforduration] with body: {"fromX":187,"fromY":201,"toX":187,"toY":151,"duration":0.2}
28+
# [debug] [WebDriverAgent] Sim: Jan 14 16:35:05 rrcs-172-254-99-35 CoreSimulatorBridge[65868]: Switching to keyboard: en
29+
#
30+
# Example for iOS's log with Instruments
31+
# [0] pry(#<device/touch_actions>)> swipe start_x: start_x, start_y: start_y, offset_x: 0, offset_y: - 50, duration: 1000
32+
# [HTTP] <-- POST /wd/hub/session/8b651f03-0fbc-43f0-aaf2-243d0650f6aa/touch/perform 200 2048 ms - 74
33+
# [HTTP] --> POST /wd/hub/session/8b651f03-0fbc-43f0-aaf2-243d0650f6aa/touch/perform {"actions":[{"action":"press","options":{"x":187,"y":201.078125}},{"action":"wait","options":{"ms":1000}},{"action":"moveTo","options":{"x":0,"y":-50}},{"action":"release"}]}
34+
# [debug] [MJSONWP] Calling AppiumDriver.performTouch() with args: [[{"action":"press","options":{"x":187,"y":201.078125}},{"action":"wait","options":{"ms":1000}},{"action":"moveTo","options":{"x":0,"y":-50}},{"action":"release"}],"8b651f03-0fbc-43f0-aaf2-243d0650f6aa"]
35+
# [debug] [iOS] Executing iOS command 'performTouch'
36+
# [debug] [UIAuto] Sending command to instruments: au.dragApp(187, 201.078125, 187, 151.078125, 1)
37+
# [debug] [Instruments] [INST] 2017-01-14 08:22:57 +0000 Debug: Got new command 12 from instruments: au.dragApp(187, 201.078125, 187, 151.078125, 1)
38+
# [debug] [Instruments] [INST] 2017-01-14 08:22:57 +0000 Debug: evaluating au.dragApp(187, 201.078125, 187, 151.078125, 1)
39+
# [debug] [Instruments] [INST] 2017-01-14 08:22:57 +0000 Debug: target.dragFromToForDuration({x:"187", y:"201.078125"}, {x:"187", y:"151.078125"}, "1")
40+
# [debug] [Instruments] [INST] 2017-01-14 08:22:58 +0000 Debug: evaluation finished
41+
# [debug] [Instruments] [INST] 2017-01-14 08:22:58 +0000 Debug: responding with:
42+
# [debug] [UIAuto] Socket data received (25 bytes)
43+
# [debug] [UIAuto] Got result from instruments: {"status":0,"value":""}
44+
# [debug] [MJSONWP] Responding to client with driver.performTouch() result: ""
45+
# [HTTP] <-- POST /wd/hub/session/8b651f03-0fbc-43f0-aaf2-243d0650f6aa/touch/perform 200 1895 ms - 74
46+
# rubocop:enable Metrics/LineLength
47+
swipe start_x: start_x, start_y: start_y, offset_x: 0, offset_y: - 50
2048
ele_index(UI::Inventory.static_text, 2).text.must_equal 'Chris Armstrong - 0'
2149
end
2250

51+
t 'swipe_coordinates_end_x_end_y' do
52+
s = Appium::TouchAction.new.swipe_coordinates(offset_x: 1, offset_y: 2)
53+
s[:offset_x].must_equal 1
54+
s[:offset_y].must_equal 2
55+
end
56+
57+
t 'swipe_coordinates_end_x' do
58+
s = Appium::TouchAction.new.swipe_coordinates(offset_x: 1)
59+
s[:offset_x].must_equal 1
60+
s[:offset_y].must_equal 0
61+
end
62+
63+
t 'swipe_coordinates_end_y' do
64+
s = Appium::TouchAction.new.swipe_coordinates(offset_y: 1)
65+
s[:offset_x].must_equal 0
66+
s[:offset_y].must_equal 1
67+
end
68+
69+
t 'swipe_coordinates_offset' do
70+
s = Appium::TouchAction.new.swipe_coordinates
71+
s[:offset_x].must_equal 0
72+
s[:offset_y].must_equal 0
73+
end
74+
2375
t 'after_last' do
2476
after_last
2577
end

lib/appium_lib/device/multi_touch.rb

+99-27
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,14 @@ class << self
2828
def pinch(percentage = 25, auto_perform = true)
2929
raise ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
3030

31-
p = Float(percentage) / 100
31+
rate = Float(percentage) / 100
3232

3333
if $driver.automation_name_is_xcuitest?
34-
ele = $driver.find_element :class, 'XCUIElementTypeApplication'
35-
top = TouchAction.new
36-
top.swipe({ start_x: 1.0, start_y: 0.0, delta_x: -p, delta_y: p }, ele)
37-
38-
bottom = TouchAction.new
39-
bottom.swipe({ start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: -p }, ele)
34+
top, bottom = pinch_for_xcuitest(rate)
35+
elsif $driver.device_is_android?
36+
top, bottom = pinch_android(rate)
4037
else
41-
top = TouchAction.new
42-
top.swipe start_x: 1.0, start_y: 0.0, delta_x: -p, delta_y: p, duration: 1
43-
44-
bottom = TouchAction.new
45-
bottom.swipe start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: -p, duration: 1
38+
top, bottom = pinch_ios(rate)
4639
end
4740

4841
pinch = MultiTouch.new
@@ -64,23 +57,14 @@ def pinch(percentage = 25, auto_perform = true)
6457
def zoom(percentage = 200, auto_perform = true)
6558
raise ArgumentError("Can't zoom to smaller then screen size.") if percentage < 100
6659

67-
p = 100 / Float(percentage)
68-
i = 1 - p
60+
rate = 100 / Float(percentage)
6961

7062
if $driver.automation_name_is_xcuitest?
71-
ele = $driver.find_element :class, 'XCUIElementTypeApplication'
72-
73-
top = TouchAction.new
74-
top.swipe({ start_x: p, start_y: i, delta_x: i, delta_y: -i }, ele)
75-
76-
bottom = TouchAction.new
77-
bottom.swipe({ start_x: i, start_y: p, delta_x: -i, delta_y: i }, ele)
63+
top, bottom = zoom_for_xcuitest(rate)
64+
elsif $driver.device_is_android?
65+
top, bottom = zoom_android(rate)
7866
else
79-
top = TouchAction.new
80-
top.swipe start_x: p, start_y: i, delta_x: i, delta_y: -i, duration: 1
81-
82-
bottom = TouchAction.new
83-
bottom.swipe start_x: i, start_y: p, delta_x: -i, delta_y: i, duration: 1
67+
top, bottom = zoom_ios(rate)
8468
end
8569

8670
zoom = MultiTouch.new
@@ -89,7 +73,95 @@ def zoom(percentage = 200, auto_perform = true)
8973
return zoom unless auto_perform
9074
zoom.perform
9175
end
92-
end
76+
77+
private
78+
79+
def pinch_for_xcuitest(rate)
80+
height = 100
81+
82+
ele = $driver.find_element :class, 'XCUIElementTypeApplication'
83+
top = TouchAction.new
84+
top.swipe({ start_x: 0.5, start_y: 0.0,
85+
offset_x: 0.0, offset_y: (1 - rate) * height }, ele)
86+
87+
bottom = TouchAction.new
88+
bottom.swipe({ start_x: 0.5, start_y: 1.0,
89+
offset_x: 0.0, offset_y: rate * height }, ele)
90+
91+
[top, bottom]
92+
end
93+
94+
def pinch_android(rate)
95+
height = 100
96+
97+
top = TouchAction.new
98+
top.swipe start_x: 0.5, start_y: 1.0 * height,
99+
end_x: 0.5, end_y: rate * height, duration: 1_000
100+
101+
bottom = TouchAction.new
102+
bottom.swipe start_x: 0.5, start_y: 0.0,
103+
end_x: 0.5, end_y: (1 - rate) * height, duration: 1_000
104+
105+
[top, bottom]
106+
end
107+
108+
def pinch_ios(rate)
109+
height = 100
110+
111+
top = TouchAction.new
112+
top.swipe start_x: 0.5, start_y: 0.0,
113+
offset_x: 0.0, offset_y: (1 - rate) * height, duration: 1_000
114+
115+
bottom = TouchAction.new
116+
bottom.swipe start_x: 0.5, start_y: 1.0,
117+
offset_x: 0.0, offset_y: rate * height, duration: 1_000
118+
119+
[top, bottom]
120+
end
121+
122+
def zoom_for_xcuitest(rate)
123+
height = 100
124+
125+
ele = $driver.find_element :class, 'XCUIElementTypeApplication'
126+
top = TouchAction.new
127+
top.swipe({ start_x: 0.5, start_y: (1 - rate) * height,
128+
offset_x: 0.0, offset_y: - (1 - rate) * height }, ele)
129+
130+
bottom = TouchAction.new
131+
bottom.swipe({ start_x: 0.5, start_y: rate * height,
132+
offset_x: 0.0, offset_y: (1 - rate) * height }, ele)
133+
134+
[top, bottom]
135+
end
136+
137+
def zoom_android(rate)
138+
height = 100
139+
140+
top = TouchAction.new
141+
top.swipe start_x: 0.5, start_y: (1.0 - rate) * height,
142+
end_x: 0.5, end_y: 0.0, duration: 1_000
143+
144+
bottom = TouchAction.new
145+
bottom.swipe start_x: 0.5, start_y: rate * height,
146+
end_x: 0.5, end_y: 1.0 * height, duration: 1_000
147+
148+
[top, bottom]
149+
end
150+
151+
def zoom_ios(rate)
152+
height = 100
153+
154+
top = TouchAction.new
155+
top.swipe start_x: 0.5, start_y: (1 - rate) * height,
156+
offset_x: 0.0, offset_y: - (1 - rate) * height, duration: 1_000
157+
158+
bottom = TouchAction.new
159+
bottom.swipe start_x: 0.5, start_y: rate * height,
160+
offset_x: 0.0, offset_y: (1 - rate) * height, duration: 1_000
161+
162+
[top, bottom]
163+
end
164+
end # self
93165

94166
# Create a new multi-action
95167
def initialize

lib/appium_lib/device/touch_actions.rb

+38-20
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ def initialize
3030
end
3131

3232
# Move to the given co-ordinates.
33-
# @option opts [integer] :x x co-ordinate to move to.
34-
# @option opts [integer] :y y co-ordinate to move to.
33+
#
34+
# `move_to`'s `x` and `y` have two case. One is working as coordinate, the other is working as offset.
35+
#
36+
# @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.
37+
# @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.
3538
# @option opts [WebDriver::Element] Element to scope this move within.
3639
def move_to(opts)
3740
opts = args_with_ele_ref(opts)
@@ -123,39 +126,38 @@ def wait(milliseconds)
123126
#
124127
# Note that iOS 7 simulators have broken swipe.
125128
#
129+
# For iOS: Use `offset_x` and `offset_y` to define the end point.
130+
#
131+
# For Android: Use `end_x` and `end_y` to define the end point.
132+
#
133+
# If you'd like more details, please read tests and its log samples in
134+
# `ios_tests/lib/ios/specs/device/touch_actions.rb` and `ios_tests/lib/ios/specs/device/touch_actions.rb`
135+
#
126136
# @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
127137
# @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
128-
# @option opts [int] :delta_x The distance from start to move, on the x axis. Default 0.
129-
# @option opts [int] :delta_y The distance from start to move, on the y axis. Default 0.
138+
# @option opts [int] :offset_x For iOS. Offset, on the x axis. Default 0.
139+
# @option opts [int] :offset_y For iOS. Offset, on the y axis. Default 0.
140+
# @option opts [int] :end_x For Android. Where to end swiping, on the x axis. Default 0.
141+
# @option opts [int] :end_y For Android. Where to end swiping, on the y axis. Default 0.
130142
# @option opts [int] :duration How long the actual swipe takes to complete in milliseconds. Default 200.
131-
# @deprecated Please do not use end_x, end_y anymore
132143
def swipe(opts, ele = nil)
133144
start_x = opts.fetch :start_x, 0
134145
start_y = opts.fetch :start_y, 0
135-
delta_x = opts.fetch :delta_x, nil
136-
delta_y = opts.fetch :delta_y, nil
146+
offset_x = opts.fetch :offset_x, nil
147+
offset_y = opts.fetch :offset_y, nil
137148
end_x = opts.fetch :end_x, nil
138149
end_y = opts.fetch :end_y, nil
139-
140-
if end_x || end_y
141-
warn '[DEPRECATION] `end_x` and `end_y` are deprecated. Please use `delta_x` and `delta_y` instead.'
142-
end
143-
144-
delta_x ||= end_x
145-
delta_y ||= end_y
146-
147-
delta_x ||= 0
148-
delta_y ||= 0
149-
150150
duration = opts.fetch :duration, 200
151151

152+
coordinates = swipe_coordinates(end_x: end_x, end_y: end_y, offset_x: offset_x, offset_y: offset_y)
153+
152154
if ele # pinch/zoom for XCUITest
153155
press x: start_x, y: start_y, element: ele
154-
move_to x: start_x + delta_x, y: start_y + delta_y, element: ele
156+
move_to x: coordinates[:offset_x], y: coordinates[:offset_y], element: ele
155157
else
156158
press x: start_x, y: start_y
157159
wait(duration) if duration
158-
move_to x: start_x + delta_x, y: start_y + delta_y
160+
move_to x: coordinates[:offset_x], y: coordinates[:offset_y]
159161
end
160162
release
161163

@@ -175,6 +177,22 @@ def cancel
175177
self
176178
end
177179

180+
def swipe_coordinates(end_x: nil, end_y: nil, offset_x: nil, offset_y: nil)
181+
if $driver.device_is_android?
182+
puts 'end_x and end_y are used for Android. Not offset_x and offset_y.' if end_x.nil? || end_y.nil?
183+
end_x ||= 0
184+
end_y ||= 0
185+
return { offset_x: end_x, offset_y: end_y }
186+
elsif offset_x.nil? || offset_y.nil?
187+
puts 'offset_x and offset_y are used for iOS. Not end_x and end_y point.'
188+
end
189+
190+
offset_x ||= 0
191+
offset_y ||= 0
192+
193+
{ offset_x: offset_x, offset_y: offset_y }
194+
end
195+
178196
private
179197

180198
def chain_method(method, args = nil)

0 commit comments

Comments
 (0)