Skip to content

fix swipe, pinch, zoom #466

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
end

t 'swipe' do
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 }
wait { Appium::TouchAction.new.swipe(start_x: 0.75, start_y: 0.25, end_x: 0.75, end_y: 50).perform }
wait { !exists { text_exact 'NFC' } }
wait { text_exact 'Bouncing Balls' }
back
Expand Down
41 changes: 40 additions & 1 deletion android_tests/lib/android/specs/device/touch_actions.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,52 @@
# rake android[device/touch_actions]
describe 'device/touch_actions' do
def swipe_till_text_visible(seen_text)
start_x = window_size[:width] / 2
start_y = window_size[:height] / 2
wait(60) do
swipe start_x: start_x, start_y: start_y, delta_x: start_x, delta_y: start_y - 100
# rubocop:disable Metrics/LineLength
# Example for Android's log
# [0] pry(#<device/touch_actions>)> swipe start_x: start_x, start_y: start_y, end_x: start_y, end_y: start_y - 100
# [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"}]}
# [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"]
# [debug] [AndroidBootstrap] Sending command to android: {"cmd":"action","action":"swipe","params":{"startX":600,"startY":919,"endX":600,"endY":819,"steps":6}}
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got data from client: {"cmd":"action","action":"swipe","params":{"startX":600,"startY":919,"endX":600,"endY":819,"steps":6}}
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command of type ACTION
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Got command action: swipe
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Display bounds: [0,0][1200,1838]
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Display bounds: [0,0][1200,1838]
# [AndroidBootstrap] [BOOTSTRAP LOG] [debug] Swiping from [x=600.0, y=919.0] to [x=600.0, y=819.0] with steps: 6
# [debug] [AndroidBootstrap] Received command result from bootstrap
# rubocop:enable Metrics/LineLength
swipe start_x: start_x, start_y: start_y, end_x: start_x, end_y: start_y - 100
text(seen_text).displayed?
end
end

t 'swipe_coordinates_end_x_end_y' do
s = Appium::TouchAction.new.swipe_coordinates(end_x: 1, end_y: 2)
s[:offset_x].must_equal 1
s[:offset_y].must_equal 2
end

t 'swipe_coordinates_end_x' do
s = Appium::TouchAction.new.swipe_coordinates(end_x: 1)
s[:offset_x].must_equal 1
s[:offset_y].must_equal 0
end

t 'swipe_coordinates_end_y' do
s = Appium::TouchAction.new.swipe_coordinates(end_y: 1)
s[:offset_x].must_equal 0
s[:offset_y].must_equal 1
end

t 'swipe_coordinates_offset' do
s = Appium::TouchAction.new.swipe_coordinates
s[:offset_x].must_equal 0
s[:offset_y].must_equal 0
end

t 'swipe_default_duration' do
swipe_till_text_visible('views')
text('views').click
Expand Down
2 changes: 1 addition & 1 deletion ios_tests/lib/ios/specs/device/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def go_back
end

t 'swipe' do
swipe start_x: 75, start_y: 500, delta_x: 0, delta_y: -500, duration: 800
swipe start_x: 75, start_y: 500, offset_x: 75, offset_y: 0, duration: 800
end

t 'pull_file' do
Expand Down
54 changes: 53 additions & 1 deletion ios_tests/lib/ios/specs/device/touch_actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,62 @@ def after_last
size = picker.size.to_h
start_x = loc[:x] + size[:width] / 2
start_y = loc[:y] + size[:height] / 2
swipe start_x: start_x, start_y: start_y, delta_x: 0, delta_y: - 50
# rubocop:disable Metrics/LineLength
# Example for iOS's log with XCUITest
# [0] pry(#<device/touch_actions>)> swipe start_x: start_x, start_y: start_y, offset_x: 0, offset_y: - 50
# [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
# [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"}]}
# [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"]
# [debug] [XCUITest] Executing command 'performTouch'
# [debug] [XCUITest] Received the following touch action: press-wait-moveTo-release
# [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}
# [debug] [WebDriverAgent] Sim: Jan 14 16:35:05 rrcs-172-254-99-35 CoreSimulatorBridge[65868]: Switching to keyboard: en
#
# Example for iOS's log with Instruments
# [0] pry(#<device/touch_actions>)> swipe start_x: start_x, start_y: start_y, offset_x: 0, offset_y: - 50, duration: 1000
# [HTTP] <-- POST /wd/hub/session/8b651f03-0fbc-43f0-aaf2-243d0650f6aa/touch/perform 200 2048 ms - 74
# [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"}]}
# [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"]
# [debug] [iOS] Executing iOS command 'performTouch'
# [debug] [UIAuto] Sending command to instruments: au.dragApp(187, 201.078125, 187, 151.078125, 1)
# [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)
# [debug] [Instruments] [INST] 2017-01-14 08:22:57 +0000 Debug: evaluating au.dragApp(187, 201.078125, 187, 151.078125, 1)
# [debug] [Instruments] [INST] 2017-01-14 08:22:57 +0000 Debug: target.dragFromToForDuration({x:"187", y:"201.078125"}, {x:"187", y:"151.078125"}, "1")
# [debug] [Instruments] [INST] 2017-01-14 08:22:58 +0000 Debug: evaluation finished
# [debug] [Instruments] [INST] 2017-01-14 08:22:58 +0000 Debug: responding with:
# [debug] [UIAuto] Socket data received (25 bytes)
# [debug] [UIAuto] Got result from instruments: {"status":0,"value":""}
# [debug] [MJSONWP] Responding to client with driver.performTouch() result: ""
# [HTTP] <-- POST /wd/hub/session/8b651f03-0fbc-43f0-aaf2-243d0650f6aa/touch/perform 200 1895 ms - 74
# rubocop:enable Metrics/LineLength
swipe start_x: start_x, start_y: start_y, offset_x: 0, offset_y: - 50
ele_index(UI::Inventory.static_text, 2).text.must_equal 'Chris Armstrong - 0'
end

t 'swipe_coordinates_end_x_end_y' do
s = Appium::TouchAction.new.swipe_coordinates(offset_x: 1, offset_y: 2)
s[:offset_x].must_equal 1
s[:offset_y].must_equal 2
end

t 'swipe_coordinates_end_x' do
s = Appium::TouchAction.new.swipe_coordinates(offset_x: 1)
s[:offset_x].must_equal 1
s[:offset_y].must_equal 0
end

t 'swipe_coordinates_end_y' do
s = Appium::TouchAction.new.swipe_coordinates(offset_y: 1)
s[:offset_x].must_equal 0
s[:offset_y].must_equal 1
end

t 'swipe_coordinates_offset' do
s = Appium::TouchAction.new.swipe_coordinates
s[:offset_x].must_equal 0
s[:offset_y].must_equal 0
end

t 'after_last' do
after_last
end
Expand Down
126 changes: 99 additions & 27 deletions lib/appium_lib/device/multi_touch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,14 @@ class << self
def pinch(percentage = 25, auto_perform = true)
raise ArgumentError("Can't pinch to greater than screen size.") if percentage > 100

p = Float(percentage) / 100
rate = Float(percentage) / 100

if $driver.automation_name_is_xcuitest?
ele = $driver.find_element :class, 'XCUIElementTypeApplication'
top = TouchAction.new
top.swipe({ start_x: 1.0, start_y: 0.0, delta_x: -p, delta_y: p }, ele)

bottom = TouchAction.new
bottom.swipe({ start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: -p }, ele)
top, bottom = pinch_for_xcuitest(rate)
elsif $driver.device_is_android?
top, bottom = pinch_android(rate)
else
top = TouchAction.new
top.swipe start_x: 1.0, start_y: 0.0, delta_x: -p, delta_y: p, duration: 1

bottom = TouchAction.new
bottom.swipe start_x: 0.0, start_y: 1.0, delta_x: p, delta_y: -p, duration: 1
top, bottom = pinch_ios(rate)
end

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

p = 100 / Float(percentage)
i = 1 - p
rate = 100 / Float(percentage)

if $driver.automation_name_is_xcuitest?
ele = $driver.find_element :class, 'XCUIElementTypeApplication'

top = TouchAction.new
top.swipe({ start_x: p, start_y: i, delta_x: i, delta_y: -i }, ele)

bottom = TouchAction.new
bottom.swipe({ start_x: i, start_y: p, delta_x: -i, delta_y: i }, ele)
top, bottom = zoom_for_xcuitest(rate)
elsif $driver.device_is_android?
top, bottom = zoom_android(rate)
else
top = TouchAction.new
top.swipe start_x: p, start_y: i, delta_x: i, delta_y: -i, duration: 1

bottom = TouchAction.new
bottom.swipe start_x: i, start_y: p, delta_x: -i, delta_y: i, duration: 1
top, bottom = zoom_ios(rate)
end

zoom = MultiTouch.new
Expand All @@ -89,7 +73,95 @@ def zoom(percentage = 200, auto_perform = true)
return zoom unless auto_perform
zoom.perform
end
end

private

def pinch_for_xcuitest(rate)
height = 100

ele = $driver.find_element :class, 'XCUIElementTypeApplication'
top = TouchAction.new
top.swipe({ start_x: 0.5, start_y: 0.0,
offset_x: 0.0, offset_y: (1 - rate) * height }, ele)

bottom = TouchAction.new
bottom.swipe({ start_x: 0.5, start_y: 1.0,
offset_x: 0.0, offset_y: rate * height }, ele)

[top, bottom]
end

def pinch_android(rate)
height = 100

top = TouchAction.new
top.swipe start_x: 0.5, start_y: 1.0 * height,
end_x: 0.5, end_y: rate * height, duration: 1_000

bottom = TouchAction.new
bottom.swipe start_x: 0.5, start_y: 0.0,
end_x: 0.5, end_y: (1 - rate) * height, duration: 1_000

[top, bottom]
end

def pinch_ios(rate)
height = 100

top = TouchAction.new
top.swipe start_x: 0.5, start_y: 0.0,
offset_x: 0.0, offset_y: (1 - rate) * height, duration: 1_000

bottom = TouchAction.new
bottom.swipe start_x: 0.5, start_y: 1.0,
offset_x: 0.0, offset_y: rate * height, duration: 1_000

[top, bottom]
end

def zoom_for_xcuitest(rate)
height = 100

ele = $driver.find_element :class, 'XCUIElementTypeApplication'
top = TouchAction.new
top.swipe({ start_x: 0.5, start_y: (1 - rate) * height,
offset_x: 0.0, offset_y: - (1 - rate) * height }, ele)

bottom = TouchAction.new
bottom.swipe({ start_x: 0.5, start_y: rate * height,
offset_x: 0.0, offset_y: (1 - rate) * height }, ele)

[top, bottom]
end

def zoom_android(rate)
height = 100

top = TouchAction.new
top.swipe start_x: 0.5, start_y: (1.0 - rate) * height,
end_x: 0.5, end_y: 0.0, duration: 1_000

bottom = TouchAction.new
bottom.swipe start_x: 0.5, start_y: rate * height,
end_x: 0.5, end_y: 1.0 * height, duration: 1_000

[top, bottom]
end

def zoom_ios(rate)
height = 100

top = TouchAction.new
top.swipe start_x: 0.5, start_y: (1 - rate) * height,
offset_x: 0.0, offset_y: - (1 - rate) * height, duration: 1_000

bottom = TouchAction.new
bottom.swipe start_x: 0.5, start_y: rate * height,
offset_x: 0.0, offset_y: (1 - rate) * height, duration: 1_000

[top, bottom]
end
end # self

# Create a new multi-action
def initialize
Expand Down
58 changes: 38 additions & 20 deletions lib/appium_lib/device/touch_actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ def initialize
end

# Move to the given co-ordinates.
# @option opts [integer] :x x co-ordinate to move to.
# @option opts [integer] :y y co-ordinate to move to.
#
# `move_to`'s `x` and `y` have two case. One is working as coordinate, the other is working as offset.
#
# @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.
# @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.
# @option opts [WebDriver::Element] Element to scope this move within.
def move_to(opts)
opts = args_with_ele_ref(opts)
Expand Down Expand Up @@ -123,39 +126,38 @@ def wait(milliseconds)
#
# Note that iOS 7 simulators have broken swipe.
#
# For iOS: Use `offset_x` and `offset_y` to define the end point.
#
# For Android: Use `end_x` and `end_y` to define the end point.
#
# If you'd like more details, please read tests and its log samples in
# `ios_tests/lib/ios/specs/device/touch_actions.rb` and `ios_tests/lib/ios/specs/device/touch_actions.rb`
#
# @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
# @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
# @option opts [int] :delta_x The distance from start to move, on the x axis. Default 0.
# @option opts [int] :delta_y The distance from start to move, on the y axis. Default 0.
# @option opts [int] :offset_x For iOS. Offset, on the x axis. Default 0.
# @option opts [int] :offset_y For iOS. Offset, on the y axis. Default 0.
# @option opts [int] :end_x For Android. Where to end swiping, on the x axis. Default 0.
# @option opts [int] :end_y For Android. Where to end swiping, on the y axis. Default 0.
# @option opts [int] :duration How long the actual swipe takes to complete in milliseconds. Default 200.
# @deprecated Please do not use end_x, end_y anymore
def swipe(opts, ele = nil)
start_x = opts.fetch :start_x, 0
start_y = opts.fetch :start_y, 0
delta_x = opts.fetch :delta_x, nil
delta_y = opts.fetch :delta_y, nil
offset_x = opts.fetch :offset_x, nil
offset_y = opts.fetch :offset_y, nil
end_x = opts.fetch :end_x, nil
end_y = opts.fetch :end_y, nil

if end_x || end_y
warn '[DEPRECATION] `end_x` and `end_y` are deprecated. Please use `delta_x` and `delta_y` instead.'
end

delta_x ||= end_x
delta_y ||= end_y

delta_x ||= 0
delta_y ||= 0

duration = opts.fetch :duration, 200

coordinates = swipe_coordinates(end_x: end_x, end_y: end_y, offset_x: offset_x, offset_y: offset_y)

if ele # pinch/zoom for XCUITest
press x: start_x, y: start_y, element: ele
move_to x: start_x + delta_x, y: start_y + delta_y, element: ele
move_to x: coordinates[:offset_x], y: coordinates[:offset_y], element: ele
else
press x: start_x, y: start_y
wait(duration) if duration
move_to x: start_x + delta_x, y: start_y + delta_y
move_to x: coordinates[:offset_x], y: coordinates[:offset_y]
end
release

Expand All @@ -175,6 +177,22 @@ def cancel
self
end

def swipe_coordinates(end_x: nil, end_y: nil, offset_x: nil, offset_y: nil)
if $driver.device_is_android?
puts 'end_x and end_y are used for Android. Not offset_x and offset_y.' if end_x.nil? || end_y.nil?
end_x ||= 0
end_y ||= 0
return { offset_x: end_x, offset_y: end_y }
elsif offset_x.nil? || offset_y.nil?
puts 'offset_x and offset_y are used for iOS. Not end_x and end_y point.'
end

offset_x ||= 0
offset_y ||= 0

{ offset_x: offset_x, offset_y: offset_y }
end

private

def chain_method(method, args = nil)
Expand Down