lib/appium_lib/driver.rb in appium_lib-9.4.9 vs lib/appium_lib/driver.rb in appium_lib-9.4.10

- old
+ new

@@ -246,10 +246,15 @@ end end nil # return nil end + def self.selenium_webdriver_version_more?(version) + require 'rubygems' + Gem.loaded_specs['selenium-webdriver'].version >= Gem::Version.new(version) + end + class Driver module Capabilities # except for browser_name, default capability is equal to ::Selenium::WebDriver::Remote::Capabilities.firefox # Because Selenium::WebDriver::Remote::Bridge uses Capabilities.firefox by default # https://github.com/SeleniumHQ/selenium/blob/selenium-3.0.1/rb/lib/selenium/webdriver/remote/bridge.rb#L67 @@ -364,35 +369,16 @@ # quit last driver $driver.driver_quit if $driver 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 + set_appium_lib_specific_values(appium_lib_opts) - # to pass it in Selenium.new. - # `listener = opts.delete(:listener)` is called in Selenium::Driver.new - @listener = appium_lib_opts.fetch :listener, nil - # Path to the .apk, .app or .app.zip. # The path can be local or remote for Sauce. if @caps && @caps[:app] && !@caps[:app].empty? @caps[:app] = self.class.absolute_app_path opts end @@ -400,20 +386,26 @@ # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile @appium_device = @caps[:platformName] @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 extend Appium::Device if device_is_android? 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 patch_webdriver_element @@ -435,13 +427,40 @@ $driver = self 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 - attributes = { + { caps: @caps, automation_name: @automation_name, custom_url: @custom_url, export_session: @export_session, default_wait: @default_wait, @@ -453,32 +472,26 @@ debug: @appium_debug, listener: @listener, wait_timeout: @appium_wait_timeout, wait_interval: @appium_wait_interval } - - # Return duplicates so attributes are immutable - attributes.each do |key, value| - attributes[key] = value.duplicable? ? value.dup : value - end - attributes end def device_is_android? @appium_device == :android end # 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. # If the Appium server is under REQUIRED_VERSION_XCUITEST, then error is raised. # @return [Boolean] @@ -510,15 +523,15 @@ # # @return [Hash] 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 # Returns the client's version info @@ -610,15 +623,37 @@ rescue nil 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 @driver = Selenium::WebDriver.for(:remote, http_client: @http_client, @@ -629,16 +664,11 @@ # Load touch methods. @driver.extend Selenium::WebDriver::DriverExtensions::HasTouchScreen @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 @appium_server_status = appium_server_version @@ -771,9 +801,16 @@ driver_quit exit # exit pry end 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 return unless @automation_name.nil?