diff --git a/docs/ios_xcuitest.md b/docs/ios_xcuitest.md index 2690cf68..96d18026 100644 --- a/docs/ios_xcuitest.md +++ b/docs/ios_xcuitest.md @@ -79,5 +79,12 @@ finds_exact(value) # Return any elements include `value` as its name attributes. xpaths("//some xpaths") ``` +## Gesture +- `mobile:` commands are provided by WDA. +- Documentations + - https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios-xctest-mobile-gestures.md +- Specs by test code + - https://github.com/appium/appium-xcuitest-driver/blob/master/test/functional/basic/gesture-e2e-specs.js + ## Other actions Basically, other actions such as `type` are compatible with `automationName = Appium`. diff --git a/ios_tests/lib/ios/specs/ios/xcuitest_gestures.rb b/ios_tests/lib/ios/specs/ios/xcuitest_gestures.rb new file mode 100644 index 00000000..b9f33f71 --- /dev/null +++ b/ios_tests/lib/ios/specs/ios/xcuitest_gestures.rb @@ -0,0 +1,61 @@ +# rake ios[ios/xcuitest_gestures] +describe 'ios/xcuitest_gestures' do + def before_first + screen.must_equal catalog + end + + def after_last + screen.must_equal catalog + end + + t 'before_first' do + before_first + end + + t 'tap' do + element = text('controls') + tap(x: 0, y: 0, element: element) + end + + t 'double_tap' do + element = button('Tinted') + double_tap(element: element) + end + + t 'scroll' do + scroll direction: 'down' + text('Style Default').displayed?.must_be true + end + + t 'swipe' do + swipe direction: 'down' + swipe direction: 'down' + + proc { text('Style Default') }.must_raise ::Selenium::WebDriver::Error::NoSuchElementError + end + + t 'pinch' do + pinch(scale: 0.5, velocity: -1) + end + + t 'back to top' do + back_click + end + + t 'select_picker_wheel' do + element = text('pickers') + tap(x: 0, y: 0, element: element) + + e = find_element :name, 'John Appleseed' + select_picker_wheel(element: e, order: 'next') + + e.displayed?.must_be false + find_element(:name, 'Serena Auroux').displayed?.must_be true + + back_click + end + + t 'after_last' do + after_last + end +end diff --git a/lib/appium_lib/driver.rb b/lib/appium_lib/driver.rb index a2736fb3..a06ad0c8 100644 --- a/lib/appium_lib/driver.rb +++ b/lib/appium_lib/driver.rb @@ -24,6 +24,7 @@ require_relative 'ios/element/textfield' require_relative 'ios/element/text' require_relative 'ios/mobile_methods' +require_relative 'ios/xcuitest_gestures' # android require_relative 'android/helper' @@ -411,6 +412,7 @@ def initialize(opts = {}) else # load iOS specific methods extend Appium::Ios + extend Appium::Ios::XcuitestGesture if automation_name_is_xcuitest? # Override touch actions end # apply os specific patches diff --git a/lib/appium_lib/ios/xcuitest_gestures.rb b/lib/appium_lib/ios/xcuitest_gestures.rb new file mode 100644 index 00000000..1903ff52 --- /dev/null +++ b/lib/appium_lib/ios/xcuitest_gestures.rb @@ -0,0 +1,144 @@ +module Appium + module Ios + module XcuitestGesture + # @param [string] direction Either 'up', 'down', 'left' or 'right'. + # @option opts [Element] :element Element to swipe on + # + # ```ruby + # swipe direction: "down" + # ``` + def swipe(direction:, element: nil) + return unless %w(up down left right).include?(direction) + + args = { direction: direction } + args[:element] = element.ref if element + + execute_script 'mobile: swipe', args + end + + # @param [string] direction Either 'up', 'down', 'left' or 'right'. + # @option opts [String] :name the accessibility id of the child element, to which scrolling is performed. + # @option opts [Element] :element Element id to long tap on. + # @option opts [bool] :to_visible Boolean parameter. If set to true then asks to scroll to the first visible + # element in the parent container. Has no effect if element is not set + # @option opts [String] :predicate_string the NSPredicate locator of the child element, + # to which the scrolling should be performed. Has no effect if element is not a container + # + # ```ruby + # scroll direction: "down" + # ``` + def scroll(direction:, name: nil, element: nil, to_visible: nil, predicate_string: nil) + return 'Set "up", "down", "left" or "right" for :direction' unless %w(up down left right).include?(direction) + + args = { direction: direction } + args[:element] = element.ref if element + args[:name] = name if name + args[:toVisible] = to_visible if to_visible + args[:predicateString] = predicate_string if predicate_string + + execute_script 'mobile: scroll', args + end + + # @param scale [scale] X tap coordinate of type float. Mandatory parameter + # @param velocity [float] Y tap coordinate of type float. Mandatory parameter + # @option opts [Element] :element Element id to long tap on. + # + # ```ruby + # pinch scale: 0.5, velocity: -1 + # ``` + def pinch(scale:, velocity: 1.0, element: nil) + return unless automation_name_is_xcuitest? + + args = { scale: scale, velocity: velocity } + args[:element] = element.ref if element + + execute_script 'mobile: pinch', args + end + + # @param x [float] X Screen x tap coordinate of type float. Mandatory parameter only if element is not set + # @param y [float] Y Screen y tap coordinate of type float. Mandatory parameter only if element is not set + # @option opts [Element] :element Element to long tap on. + # + # ```ruby + # double_tap x: 100, y: 100 + # double_tap element: find_element(:accessibility_id, "some item") + # ``` + def double_tap(x: nil, y: nil, element: nil) + return 'require XCUITest(WDA)' unless automation_name_is_xcuitest? + return 'Set x, y or element' if (x.nil? || y.nil?) && element.nil? + + args = element.nil? ? { x: x, y: y } : { element: element.ref } + execute_script 'mobile: doubleTap', args + end + + # @param [Element] :element Element to long tap on. + # + # ```ruby + # two_finger_tap element: find_element(:accessibility_id, "some item") + # ``` + def two_finger_tap(element:) + return 'require XCUITest(WDA)' unless automation_name_is_xcuitest? + + args = { element: element.ref } + execute_script 'mobile: twoFingerTap', args + end + + # @param x [float] X tap coordinate of type float. Mandatory parameter + # @param y [float] Y tap coordinate of type float. Mandatory parameter + # @option opts [Element] :element Element id to long tap on. x and y tap coordinates will be calculated + # relatively to the current element position on the screen if this argument is provided. + # Otherwise they should be calculated relatively to screen borders. + # + # ```ruby + # tap x: 100, y: 100 + # tap x: 100, y: 100, element: find_element(:accessibility_id, "some item") + # ``` + def tap(x:, y:, element: nil) + return 'require XCUITest(WDA)' unless automation_name_is_xcuitest? + + args = { x: x, y: y } + args[:element] = element.ref + execute_script 'mobile: tap', args + end + + # rubocop:disable Metrics/ParameterLists + # @param duration [float] Float number of seconds in range [0.5, 60]. How long the tap gesture at starting + # drag point should be before to start dragging. Mandatory parameter + # @param from_x [float] The x coordinate of starting drag point (type float). Mandatory parameter + # @param from_y [float] The y coordinate of starting drag point (type float). Mandatory parameter + # @param to_x [float] The x coordinate of ending drag point (type float). Mandatory parameter + # @param to_y [float] The y coordinate of ending drag point (type float). Mandatory parameter + # @option opts [Element] :element Element id to perform drag on. All the coordinates will be calculated + # relatively this this element position on the screen. Absolute screen coordinates are expected + # if this argument is not set + # + # ```ruby + # drag_from_to_for_duration from_x: 100, from_y: 100, to_x: 150, to_y: 150 + # ``` + def drag_from_to_for_duration(from_x:, from_y:, to_x:, to_y:, duration: 1.0, element: nil) + return 'require XCUITest(WDA)' unless automation_name_is_xcuitest? + + args = { from_x: from_x, from_y: from_y, to_x: to_x, to_y: to_y, duration: duration } + args[:element] = element if element + execute_script 'mobile: dragFromToForDuration', args + end + # rubocop:enable Metrics/ParameterLists + + # https://github.com/facebook/WebDriverAgent/pull/523 + # https://github.com/appium/appium-xcuitest-driver/pull/420 + # @param order [String] The order to move picker to. "next" or "previous". + # @param element [Element] Element id to perform select picker wheel on. + # + # ```ruby + # select_picker_wheel order: "next", element: find_element(:accessibility_id, "some picker") + # ``` + def select_picker_wheel(element:, order:) + return 'require XCUITest(WDA)' unless automation_name_is_xcuitest? + return 'Set "next" or "previous" for :order' unless %w(next previous).include?(order) + + args = { element: element.ref, order: order } + execute_script 'mobile: selectPickerWheelValue', args + end + end # module XcuitestGesture + end # module Ios +end # module Appium