diff --git a/android_tests/lib/android/specs/common/device_touchaction.rb b/android_tests/lib/android/specs/common/device_touchaction.rb index d877ab03..46398b62 100644 --- a/android_tests/lib/android/specs/common/device_touchaction.rb +++ b/android_tests/lib/android/specs/common/device_touchaction.rb @@ -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 diff --git a/android_tests/lib/android/specs/device/touch_actions.rb b/android_tests/lib/android/specs/device/touch_actions.rb index 462e490e..77fd7d3d 100644 --- a/android_tests/lib/android/specs/device/touch_actions.rb +++ b/android_tests/lib/android/specs/device/touch_actions.rb @@ -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(#)> 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 diff --git a/ios_tests/lib/ios/specs/device/device.rb b/ios_tests/lib/ios/specs/device/device.rb index be261966..47bffe37 100644 --- a/ios_tests/lib/ios/specs/device/device.rb +++ b/ios_tests/lib/ios/specs/device/device.rb @@ -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 diff --git a/ios_tests/lib/ios/specs/device/touch_actions.rb b/ios_tests/lib/ios/specs/device/touch_actions.rb index fce38a96..ef216972 100644 --- a/ios_tests/lib/ios/specs/device/touch_actions.rb +++ b/ios_tests/lib/ios/specs/device/touch_actions.rb @@ -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(#)> 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(#)> 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 diff --git a/lib/appium_lib/device/multi_touch.rb b/lib/appium_lib/device/multi_touch.rb index 47a6fee0..b4190135 100644 --- a/lib/appium_lib/device/multi_touch.rb +++ b/lib/appium_lib/device/multi_touch.rb @@ -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 @@ -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 @@ -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 diff --git a/lib/appium_lib/device/touch_actions.rb b/lib/appium_lib/device/touch_actions.rb index fd07edff..649223fe 100644 --- a/lib/appium_lib/device/touch_actions.rb +++ b/lib/appium_lib/device/touch_actions.rb @@ -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) @@ -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 @@ -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)