diff --git a/android_tests/lib/android/specs/driver.rb b/android_tests/lib/android/specs/driver.rb index 71bf8f2d..ead84439 100644 --- a/android_tests/lib/android/specs/driver.rb +++ b/android_tests/lib/android/specs/driver.rb @@ -35,7 +35,7 @@ def sauce? end t 'verify Appium::Driver::Capabilities.init_caps_for_appium' do - expected_app = File.absolute_path('api.apk') + expected_app = File.absolute_path('../test_apps/api.apk') caps = ::Appium::Driver::Capabilities.init_caps_for_appium(platformName: 'Android', app: expected_app, appPackage: 'io.appium.android.apis', diff --git a/ios_tests/lib/ios/specs/driver.rb b/ios_tests/lib/ios/specs/driver.rb index c01d4ef9..c307b4c9 100644 --- a/ios_tests/lib/ios/specs/driver.rb +++ b/ios_tests/lib/ios/specs/driver.rb @@ -58,9 +58,9 @@ def sauce? t 'verify all attributes' do actual = driver_attributes caps_app_for_teardown = actual[:caps][:app] - expected_app = File.absolute_path('UICatalog.app') + expected_app = File.absolute_path('../test_apps/UICatalog.app') - expected = { automation_name: 'XCUITest', + expected = { automation_name: :xcuitest, custom_url: false, export_session: false, default_wait: 30, diff --git a/lib/appium_lib/common/error.rb b/lib/appium_lib/common/error.rb index 578615b6..f8efa5a0 100644 --- a/lib/appium_lib/common/error.rb +++ b/lib/appium_lib/common/error.rb @@ -1,5 +1,8 @@ module Appium module Error class NotSupportedAppiumServer < RuntimeError; end + + # Server side error + class ServerError; end end end diff --git a/lib/appium_lib/driver.rb b/lib/appium_lib/driver.rb index 696932f4..2cc9ba39 100644 --- a/lib/appium_lib/driver.rb +++ b/lib/appium_lib/driver.rb @@ -371,30 +371,11 @@ def initialize(opts = {}) raise 'opts must be a hash' unless opts.is_a? Hash opts = Appium.symbolize_keys opts - @caps = Capabilities.init_caps_for_appium(opts[:caps] || {}) appium_lib_opts = opts[:appium_lib] || {} - # appium_lib specific values - @custom_url = appium_lib_opts.fetch :server_url, false - @export_session = appium_lib_opts.fetch :export_session, false - @default_wait = appium_lib_opts.fetch :wait, 0 - @sauce_username = appium_lib_opts.fetch :sauce_username, ENV['SAUCE_USERNAME'] - @sauce_username = nil if !@sauce_username || (@sauce_username.is_a?(String) && @sauce_username.empty?) - @sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY'] - @sauce_access_key = nil if !@sauce_access_key || (@sauce_access_key.is_a?(String) && @sauce_access_key.empty?) - @sauce_endpoint = appium_lib_opts.fetch :sauce_endpoint, ENV['SAUCE_ENDPOINT'] - @sauce_endpoint = 'ondemand.saucelabs.com:443/wd/hub' if - !@sauce_endpoint || (@sauce_endpoint.is_a?(String) && @sauce_endpoint.empty?) - @appium_port = appium_lib_opts.fetch :port, 4723 - # timeout and interval used in ::Appium::Comm.wait/wait_true - @appium_wait_timeout = appium_lib_opts.fetch :wait_timeout, 30 - @appium_wait_interval = appium_lib_opts.fetch :wait_interval, 0.5 - - # to pass it in Selenium.new. - # `listener = opts.delete(:listener)` is called in Selenium::Driver.new - @listener = appium_lib_opts.fetch :listener, nil + set_appium_lib_specific_values(appium_lib_opts) # Path to the .apk, .app or .app.zip. # The path can be local or remote for Sauce. @@ -407,6 +388,9 @@ def initialize(opts = {}) @appium_device = @appium_device.is_a?(Symbol) ? @appium_device : @appium_device.downcase.strip.intern if @appium_device @automation_name = @caps[:automationName] if @caps[:automationName] + @automation_name = if @automation_name + @automation_name.is_a?(Symbol) ? @automation_name : @automation_name.downcase.strip.intern + end # load common methods extend Appium::Common @@ -416,7 +400,10 @@ def initialize(opts = {}) extend Appium::Android else extend Appium::Ios - extend Appium::Ios::XcuitestGesture if automation_name_is_xcuitest? # Override touch actions + if automation_name_is_xcuitest? # Override touch actions + extend Appium::Ios::Xcuitest + extend Appium::Ios::Xcuitest::Gesture + end end # apply os specific patches @@ -442,6 +429,33 @@ def initialize(opts = {}) self # return newly created driver end + private + + def set_appium_lib_specific_values(appium_lib_opts) + @custom_url = appium_lib_opts.fetch :server_url, false + @export_session = appium_lib_opts.fetch :export_session, false + @default_wait = appium_lib_opts.fetch :wait, 0 + + @sauce_username = appium_lib_opts.fetch :sauce_username, ENV['SAUCE_USERNAME'] + @sauce_username = nil if !@sauce_username || (@sauce_username.is_a?(String) && @sauce_username.empty?) + @sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY'] + @sauce_access_key = nil if !@sauce_access_key || (@sauce_access_key.is_a?(String) && @sauce_access_key.empty?) + @sauce_endpoint = appium_lib_opts.fetch :sauce_endpoint, ENV['SAUCE_ENDPOINT'] + @sauce_endpoint = 'ondemand.saucelabs.com:443/wd/hub' if + !@sauce_endpoint || (@sauce_endpoint.is_a?(String) && @sauce_endpoint.empty?) + + @appium_port = appium_lib_opts.fetch :port, 4723 + # timeout and interval used in ::Appium::Comm.wait/wait_true + @appium_wait_timeout = appium_lib_opts.fetch :wait_timeout, 30 + @appium_wait_interval = appium_lib_opts.fetch :wait_interval, 0.5 + + # to pass it in Selenium.new. + # `listener = opts.delete(:listener)` is called in Selenium::Driver.new + @listener = appium_lib_opts.fetch :listener, nil + end + + public + # Returns a hash of the driver attributes def driver_attributes { @@ -469,13 +483,13 @@ def device_is_android? # Return true if automationName is 'XCUITest' # @return [Boolean] def automation_name_is_xcuitest? - !@automation_name.nil? && 'xcuitest'.casecmp(@automation_name).zero? + !@automation_name.nil? && @automation_name == :xcuitest end # Return true if automationName is 'uiautomator2' # @return [Boolean] def automation_name_is_uiautomator2? - !@automation_name.nil? && 'uiautomator2'.casecmp(@automation_name).zero? + !@automation_name.nil? && @automation_name == :uiautomator2 end # Return true if the target Appium server is over REQUIRED_VERSION_XCUITEST. @@ -511,11 +525,11 @@ def check_server_version_xcuitest def appium_server_version driver.remote_status rescue Selenium::WebDriver::Error::WebDriverError => ex - raise unless ex.message.include?('content-type=""') + raise ::Appium::Error::ServerError unless ex.message.include?('content-type=""') # server (TestObject for instance) does not respond to status call {} rescue Selenium::WebDriver::Error::ServerError => e - raise unless e.message.include?('status code 500') + raise ::Appium::Error::ServerError unless e.message.include?('status code 500') # driver.remote_status returns 500 error for using selenium grid {} end @@ -611,11 +625,33 @@ def driver_quit end # Creates a new global driver and quits the old one if it exists. + # You can customise http_client as the following + # + # @example + # ```ruby + # require 'rubygems' + # require 'appium_lib' + # + # # platformName takes a string or a symbol. + # + # # Start iOS driver + # opts = { + # caps: { + # platformName: :ios, + # app: '/path/to/MyiOS.app' + # }, + # appium_lib: { + # wait_timeout: 30 + # } + # } + # custom_http_client = Custom::Http::Client.new(opts) + # Appium::Driver.new(opts).start_driver(custom_http_client) # # @return [Selenium::WebDriver] the new global driver - def start_driver + def start_driver(http_client = + Selenium::WebDriver::Remote::Http::Default.new(open_timeout: 999_999, read_timeout: 999_999)) # open_timeout and read_timeout are explicit wait. - @http_client ||= Selenium::WebDriver::Remote::Http::Default.new(open_timeout: 999_999, read_timeout: 999_999) + @http_client ||= http_client begin driver_quit @@ -630,12 +666,7 @@ def start_driver @driver.extend Selenium::WebDriver::DriverExtensions::HasLocation # export session - if @export_session - # rubocop:disable Style/RescueModifier - File.open('/tmp/appium_lib_session', 'w') do |f| - f.puts @driver.session_id - end rescue nil - end + write_session_id(@driver.session_id) if @export_session rescue Errno::ECONNREFUSED raise "ERROR: Unable to connect to Appium. Is the server running on #{server_url}?" end @@ -773,6 +804,13 @@ def x private + def write_session_id(session_id) + File.open('/tmp/appium_lib_session', 'w') { |f| f.puts session_id } + rescue IOError => e + ::Appium::Logger.warn e + nil + end + # If "automationName" is set only server side, this method set "automationName" attribute into @automation_name. # Since @automation_name is set only client side before start_driver is called. def set_automation_name_if_nil diff --git a/lib/appium_lib/ios/mobile_methods.rb b/lib/appium_lib/ios/mobile_methods.rb index d740f9f0..dbfd3488 100644 --- a/lib/appium_lib/ios/mobile_methods.rb +++ b/lib/appium_lib/ios/mobile_methods.rb @@ -16,24 +16,30 @@ class << self # find_elements :predicate, 'wdName == "Buttons"' # find_elements :predicate, 'wdValue == "SearchBar" AND isWDDivisible == 1' # ``` - # - # @!method ios_class_chain_find - # Only for XCUITest(WebDriverAgent) - # find_element/s can be used with a [class chain]( https://github.com/facebook/WebDriverAgent/wiki/Queries) - # - # ```ruby - # # select the third child button of the first child window element - # find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]' - # # select all the children windows - # find_elements :class_chain, 'XCUIElementTypeWindow' - # # select the second last child of the second child window - # find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]' - # ``` def extended(_mod) ::Appium::Driver::SearchContext::FINDERS[:uiautomation] = '-ios uiautomation' ::Appium::Driver::SearchContext::FINDERS[:predicate] = '-ios predicate string' - ::Appium::Driver::SearchContext::FINDERS[:class_chain] = '-ios class chain' end end # class << self + + module Xcuitest + class << self + # @!method ios_class_chain_find + # Only for XCUITest(WebDriverAgent) + # find_element/s can be used with a [class chain]( https://github.com/facebook/WebDriverAgent/wiki/Queries) + # + # ```ruby + # # select the third child button of the first child window element + # find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]' + # # select all the children windows + # find_elements :class_chain, 'XCUIElementTypeWindow' + # # select the second last child of the second child window + # find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]' + # ``` + def extended(_mod) + ::Appium::Driver::SearchContext::FINDERS[:class_chain] = '-ios class chain' + end + end + end end # module Ios end # module Appium diff --git a/lib/appium_lib/ios/xcuitest_gestures.rb b/lib/appium_lib/ios/xcuitest_gestures.rb index 5178907e..257b1a68 100644 --- a/lib/appium_lib/ios/xcuitest_gestures.rb +++ b/lib/appium_lib/ios/xcuitest_gestures.rb @@ -1,184 +1,186 @@ 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 x [float] Screen x long tap coordinate of type float. Mandatory parameter only if element is not set - # @param y [float] Screen y long tap coordinate of type float. Mandatory parameter only if element is not set - # @param duration [Float] The float duration of press action in seconds. Mandatory parameter - # @option opts [Element] :element The internal element identifier (as hexadecimal hash string) to long tap on - # - # ```ruby - # touch_and_hold x: 100, y: 100 - # touch_and_hold x: 100, y: 100, duration: 2.0 - # touch_and_hold element: find_element(:accessibility_id, "some item") - # ``` - def touch_and_hold(x: nil, y: nil, element: nil, duration: 1.0) - 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 } - args[:duration] = duration - execute_script 'mobile: touchAndHold', 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 if element - 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 = { fromX: from_x, fromY: from_y, toX: to_x, toY: to_y, duration: duration } - args[:element] = element.ref 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. - # @option opts [Integer] :offset The value in range [0.01, 0.5]. Default is 0.2 in server side. - # https://github.com/facebook/WebDriverAgent/pull/549/files - # - # ```ruby - # select_picker_wheel order: "next", element: find_element(:accessibility_id, "some picker") - # ``` - def select_picker_wheel(element:, order:, offset: nil) - 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 } - args[:offset] = offset if offset - execute_script 'mobile: selectPickerWheelValue', args - end - - # @param action [String] The following actions are supported: accept, dismiss and getButtons. Mandatory parameter - # @param button_label [String] The label text of an existing alert button to click on. - # This is an optional parameter and is only valid in combination with accept and dismiss actions. - # @return {} or Selenium::WebDriver::Error::NoSuchAlertError if no action sheet or alert - # or button labels if action is equal to getButtons. - # - # ```ruby - # alert action: "accept" - # alert action: "dismiss" - # ``` - def alert(action:, button_label: nil) - return 'Set "accept", "dismiss" or "getButtons" for :action' unless %w(accept dismiss getButtons).include?(action) - - args = { action: action } - args[:button_label] if button_label - execute_script 'mobile: alert', args - end - end # module XcuitestGesture + module Xcuitest + module Gesture + # @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 x [float] Screen x long tap coordinate of type float. Mandatory parameter only if element is not set + # @param y [float] Screen y long tap coordinate of type float. Mandatory parameter only if element is not set + # @param duration [Float] The float duration of press action in seconds. Mandatory parameter + # @option opts [Element] :element The internal element identifier (as hexadecimal hash string) to long tap on + # + # ```ruby + # touch_and_hold x: 100, y: 100 + # touch_and_hold x: 100, y: 100, duration: 2.0 + # touch_and_hold element: find_element(:accessibility_id, "some item") + # ``` + def touch_and_hold(x: nil, y: nil, element: nil, duration: 1.0) + 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 } + args[:duration] = duration + execute_script 'mobile: touchAndHold', 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 if element + 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 = { fromX: from_x, fromY: from_y, toX: to_x, toY: to_y, duration: duration } + args[:element] = element.ref 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. + # @option opts [Integer] :offset The value in range [0.01, 0.5]. Default is 0.2 in server side. + # https://github.com/facebook/WebDriverAgent/pull/549/files + # + # ```ruby + # select_picker_wheel order: "next", element: find_element(:accessibility_id, "some picker") + # ``` + def select_picker_wheel(element:, order:, offset: nil) + 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 } + args[:offset] = offset if offset + execute_script 'mobile: selectPickerWheelValue', args + end + + # @param action [String] The following actions are supported: accept, dismiss and getButtons. Mandatory parameter + # @param button_label [String] The label text of an existing alert button to click on. + # This is an optional parameter and is only valid in combination with accept and dismiss actions. + # @return {} or Selenium::WebDriver::Error::NoSuchAlertError if no action sheet or alert + # or button labels if action is equal to getButtons. + # + # ```ruby + # alert action: "accept" + # alert action: "dismiss" + # ``` + def alert(action:, button_label: nil) + return 'Set "accept", "dismiss" or "getButtons" for :action' unless %w(accept dismiss getButtons).include?(action) + + args = { action: action } + args[:button_label] if button_label + execute_script 'mobile: alert', args + end + end # module Gesture + end # module Xcuitest end # module Ios end # module Appium