Class: Appium::Core::Driver

Inherits:
Object
  • Object
show all
Includes:
Waitable
Defined in:
lib/appium_lib_core/driver.rb

Constant Summary collapse

DEFAULT_APPIUM_PORT =
4723

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Waitable

#wait_until, #wait_until_true

Instance Attribute Details

#automation_nameHash (readonly)

Automation name sent to appium server or received by server.
If automation_name is nil, it is not set both client side and server side.

Returns:

  • (Hash)


133
134
135
# File 'lib/appium_lib_core/driver.rb', line 133

def automation_name
  @automation_name
end

#capsCore::Base::Capabilities (readonly)

Selenium webdriver capabilities



113
114
115
# File 'lib/appium_lib_core/driver.rb', line 113

def caps
  @caps
end

#custom_urlString (readonly)

Custom URL for the selenium server. If set this attribute, ruby_lib_core try to handshake to the custom url.
Defaults to false. Then try to connect to http://127.0.0.1:#{port}/wd/hub.

Returns:

  • (String)


138
139
140
# File 'lib/appium_lib_core/driver.rb', line 138

def custom_url
  @custom_url
end

#default_waitInteger (readonly)

Default wait time for elements to appear in Appium server side. Provide { appium_lib: { wait: 30 } } to Appium::Core.for

Returns:

  • (Integer)


149
150
151
# File 'lib/appium_lib_core/driver.rb', line 149

def default_wait
  @default_wait
end

#deviceSymbol (readonly)

Device type to request from the appium server

Returns:

  • (Symbol)

    :android and :ios, for example



128
129
130
# File 'lib/appium_lib_core/driver.rb', line 128

def device
  @device
end

#direct_connectBool (readonly)

[Experimental feature]
Enable an experimental feature updating Http client endpoint following below keys by Appium/Selenium server.
This works with Base::Http::Default.

If your Selenium/Appium server decorates the new session capabilities response with the following keys:

  • directConnectProtocol

  • directConnectHost

  • directConnectPort

  • directConnectPath

ignore them if this parameter is false. Defaults to true. These keys can have appium: prefix.

Returns:

  • (Bool)


191
192
193
# File 'lib/appium_lib_core/driver.rb', line 191

def direct_connect
  @direct_connect
end

#driverAppium::Core::Base::Driver (readonly)



175
176
177
# File 'lib/appium_lib_core/driver.rb', line 175

def driver
  @driver
end

#enable_idempotency_headerBool (readonly)

Return if adding ‘x-idempotency-key’ header is enabled for each new session request. Following commands should not have the key. The key is unique for each http client instance. Defaults to true github.com/appium/appium-base-driver/pull/400

Returns:

  • (Bool)


124
125
126
# File 'lib/appium_lib_core/driver.rb', line 124

def enable_idempotency_header
  @enable_idempotency_header
end

#export_sessionBoolean (readonly)

Export session id to textfile in /tmp for 3rd party tools. False by default.

Returns:

  • (Boolean)


142
143
144
# File 'lib/appium_lib_core/driver.rb', line 142

def export_session
  @export_session
end

#export_session_pathString (readonly)

Returns By default, session id is exported in ‘/tmp/appium_lib_session’.

Returns:

  • (String)

    By default, session id is exported in ‘/tmp/appium_lib_session’



144
145
146
# File 'lib/appium_lib_core/driver.rb', line 144

def export_session_path
  @export_session_path
end

#http_clientAppium::Core::Base::Http::Default (readonly)

Return http client called in start_driver()

Returns:



117
118
119
# File 'lib/appium_lib_core/driver.rb', line 117

def http_client
  @http_client
end

#listenerObject (readonly)

instance of AbstractEventListener for logging support Nil by default



172
173
174
# File 'lib/appium_lib_core/driver.rb', line 172

def listener
  @listener
end

#portInteger (readonly)

Appium’s server port. 4723 is by default. Defaults to DEFAULT_APPIUM_PORT.
Provide { appium_lib: { port: 8080 } } to Appium::Core.for. :custom_url is prior than :port if :custom_url is set.

Returns:

  • (Integer)


155
156
157
# File 'lib/appium_lib_core/driver.rb', line 155

def port
  @port
end

#wait_intervalInteger (readonly)

Return a time to wait interval. 0.5 seconds is by default Wait::DEFAULT_INTERVAL.
Wait interval time for Base::Wait, wait and wait_true.
Provide { appium_lib: { wait_interval: 0.1 } } to Appium::Core.for.

Returns:

  • (Integer)


168
169
170
# File 'lib/appium_lib_core/driver.rb', line 168

def wait_interval
  @wait_interval
end

#wait_timeoutInteger (readonly)

Return a time wait timeout. 30 seconds is by default Wait::DEFAULT_TIMEOUT.
Wait time for Base::Wait, wait and wait_true.
Provide { appium_lib: { wait_timeout: 20 } } to Appium::Core.for.

Returns:

  • (Integer)


162
163
164
# File 'lib/appium_lib_core/driver.rb', line 162

def wait_timeout
  @wait_timeout
end

Class Method Details

.attach_to(session_id, url: nil, automation_name: nil, platform_name: nil, http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }) ⇒ Selenium::WebDriver

Attach to an existing session. The main usage of this method is to attach to an existing session for debugging. The generated driver instance has the capabilities which has the given automationName and platformName only since the W3C WebDriver spec does not provide an endpoint to get running session’s capabilities.

Examples:


new_driver = ::Appium::Core::Driver.attach_to(
  driver.session_id,  # The 'driver' has an existing session id
  url: 'http://127.0.0.1:4723/wd/hub', automation_name: 'UiAutomator2', platform_name: 'Android'
)
new_driver.page_source # for example

Parameters:

  • The (String)

    session id to attach to.

  • url (String) (defaults to: nil)

    The WebDriver URL to attach to with the session_id.

  • automation_name (String) (defaults to: nil)

    The platform name to keep in the dummy capabilities

  • platform_name (String) (defaults to: nil)

    The automation name to keep in the dummy capabilities

Returns:

  • (Selenium::WebDriver)

    A new driver instance with the given session id.



320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/appium_lib_core/driver.rb', line 320

def self.attach_to(
  session_id, url: nil, automation_name: nil, platform_name: nil,
  http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }
)
  new.attach_to(
    session_id,
    automation_name: automation_name,
    platform_name: platform_name,
    url: url,
    http_client_ops: http_client_ops
  )
end

.for(opts = {}) ⇒ Driver

Creates a new driver and extend particular methods

Examples:


# format 1
@core = Appium::Core.for caps: {...}, appium_lib: {...}
# format 2. 'capabilities:' is also available instead of 'caps:'.
@core = Appium::Core.for url: "http://127.0.0.1:8080/wd/hub", capabilities: {...}, appium_lib: {...}

require 'rubygems'
require 'appium_lib_core'

# Start iOS driver
opts = {
         caps: {
           platformName: :ios,
           platformVersion: '11.0',
           deviceName: 'iPhone Simulator',
           automationName: 'XCUITest',
           app: '/path/to/MyiOS.app'
         },
         appium_lib: {
           export_session: false,
           port: 8080,
           wait: 0,
           wait_timeout: 20,
           wait_interval: 0.3,
           listener: nil,
         }
       }
@core = Appium::Core.for(opts) # create a core driver with 'opts' and extend methods into 'self'
@core.start_driver # Connect to 'http://127.0.0.1:8080/wd/hub' because of 'port: 8080'

# Start iOS driver with .zip file over HTTP
# 'capabilities:' is also available instead of 'caps:'. Either is fine.
opts = {
         capabilities: {
           platformName: :ios,
           platformVersion: '11.0',
           deviceName: 'iPhone Simulator',
           automationName: 'XCUITest',
           app: 'http://example.com/path/to/MyiOS.app.zip'
         },
         appium_lib: {
           server_url: 'http://custom-host:8080/wd/hub.com',
           export_session: false,
           wait: 0,
           wait_timeout: 20,
           wait_interval: 0.3,
           listener: nil,
         }
       }
@core = Appium::Core.for(opts)
@core.start_driver # Connect to 'http://custom-host:8080/wd/hub.com'

# Start iOS driver as another format. 'url' is available like below
opts = {
         url: "http://custom-host:8080/wd/hub.com",
         capabilities: {
           platformName: :ios,
           platformVersion: '11.0',
           deviceName: 'iPhone Simulator',
           automationName: 'XCUITest',
           app: '/path/to/MyiOS.app'
         },
         appium_lib: {
           export_session: false,
           wait: 0,
           wait_timeout: 20,
           wait_interval: 0.3,
           listener: nil,
         }
       }
@core = Appium::Core.for(opts) # create a core driver with 'opts' and extend methods into 'self'
@core.start_driver # start driver with 'url'. Connect to 'http://custom-host:8080/wd/hub.com'

# With a custom listener
class CustomListener < ::Selenium::WebDriver::Support::AbstractEventListener
  // something
end
capabilities: {
  platformName: :ios,
  platformVersion: '11.0',
  deviceName: 'iPhone Simulator',
  automationName: 'XCUITest',
  app: '/path/to/MyiOS.app'
},
appium_lib: {
  listener: CustomListener.new,
}
@core = Appium::Core.for capabilities: capabilities, appium_lib: appium_lib
@core.start_driver

Parameters:

  • opts (Hash) (defaults to: {})

    A options include capabilities for the Appium Server and for the client.

Options Hash (opts):

  • :caps (Hash)

    Appium capabilities.

  • :capabilities (Hash)

    The same as :caps. This param is for compatibility with Selenium WebDriver format

  • :appium_lib (Appium::Core::Options)

    Capabilities affect only ruby client

  • :url (String)

    The same as :custom_url in :appium_lib. This param is for compatibility with Selenium WebDriver format

Returns:



296
297
298
# File 'lib/appium_lib_core/driver.rb', line 296

def self.for(opts = {})
  new.setup_for_new_session(opts)
end

Instance Method Details

#appium_server_versionHash

Returns the server’s version info. This method calls driver.remote_status internally

Returns blank hash in a case driver.remote_status got an error such as Selenium Grid. It returns 500 error against ‘remote_status’.

Examples:


@core.appium_server_version
  {
      "build" => {
          "version" => "1.9.2",
          "git-sha" => "fd7c7fd19d3896719616cd970229d73e63b5271a",
          "built" => "2018-11-08 12:24:02 +0900"
      }
  }

@core.appium_server_version #=> {}

Returns:

  • (Hash)


561
562
563
564
565
566
567
568
569
# File 'lib/appium_lib_core/driver.rb', line 561

def appium_server_version
  return {} if @driver.nil?

  @driver.remote_status
rescue StandardError
  # Ignore error case in a case the target appium server
  # does not support `/status` API.
  {}
end

#attach_to(session_id, url: nil, automation_name: nil, platform_name: nil, http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }) ⇒ Object

Attach to an existing session



467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/appium_lib_core/driver.rb', line 467

def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
              http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })

  raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
  raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
  raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?

  @custom_url = url

  # use lowercase internally
  @automation_name = convert_downcase(automation_name)
  @device = convert_downcase(platform_name)

  extend_for(device: @device, automation_name: @automation_name)

  @http_client = get_http_client http_client: http_client_ops.delete(:http_client),
                                 open_timeout: http_client_ops.delete(:open_timeout),
                                 read_timeout: http_client_ops.delete(:read_timeout)

  # Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
  # the new session request, this it does not needed.

  begin
    # included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
    @driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
                                               url: @custom_url,
                                               listener: @listener,
                                               existing_session_id: session_id,
                                               automation_name: automation_name,
                                               platform_name: platform_name)

    # export session
    write_session_id(@driver.session_id, @export_session_path) if @export_session
  rescue Errno::ECONNREFUSED
    raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
  end

  @driver
end

#get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil) ⇒ Object



507
508
509
# File 'lib/appium_lib_core/driver.rb', line 507

def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
  http_client || Appium::Core::Base::Http::Default.new(open_timeout: open_timeout, read_timeout: read_timeout)
end

#platform_versionArray<Integer>

Return the platform version as an array of integers

Examples:


@core.platform_version #=> [10,1,1]

Returns:

  • (Array<Integer>)


578
579
580
581
582
583
584
585
586
# File 'lib/appium_lib_core/driver.rb', line 578

def platform_version
  ::Appium::Logger.warn(
    '[DEPRECATION] platform_version method will be. ' \
    'Please check the platformVersion via @driver.capabilities["platformVersion"] instead.'
  )

  p_version = @driver.capabilities['platformVersion'] || @driver.session_capabilities['platformVersion']
  p_version.split('.').map(&:to_i)
end

#quit_drivervoid

This method returns an undefined value.

Deprecated

Quits the driver. This method is the same as @driver.quit

Examples:


@core.quit_driver


532
533
534
535
536
537
# File 'lib/appium_lib_core/driver.rb', line 532

def quit_driver
  ::Appium::Logger.warn('[DEPRECATION] quit_driver will be removed. Please use @driver.quit instead.')
  @driver.quit
rescue # rubocop:disable Style/RescueStandardError
  nil
end

#set_implicit_wait_by_default(wait) ⇒ Object

Ignore setting default wait if the target driver has no implementation



512
513
514
515
516
517
518
519
520
521
522
523
# File 'lib/appium_lib_core/driver.rb', line 512

def set_implicit_wait_by_default(wait)
  return if @default_wait.nil?

  @driver.manage.timeouts.implicit_wait = wait
rescue ::Selenium::WebDriver::Error::UnknownError => e
  unless e.message.include?('The operation requested is not yet implemented')
    raise ::Appium::Core::Error::ServerError, e.message
  end

  ::Appium::Logger.debug(e.message)
  {}
end

#setup_for_new_session(opts = {}) ⇒ Object

Set up for a new session



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/appium_lib_core/driver.rb', line 351

def setup_for_new_session(opts = {})
  @custom_url = opts.delete :url # to set the custom url as :url

  # TODO: Remove when we implement Options
  # The symbolize_keys is to keep compatiility for the legacy code, which allows capabilities to give 'string' as the key.
  # The toplevel `caps`, `capabilities` and `appium_lib` are expected to be symbol.
  # FIXME: First, please try to remove `nested: true` to `nested: false`.
  opts = Appium.symbolize_keys(opts, nested: true)

  @caps = get_caps(opts)

  set_appium_lib_specific_values(get_appium_lib_opts(opts))
  set_app_path
  set_appium_device
  set_automation_name

  extend_for(device: @device, automation_name: @automation_name)
  self
end

#start_driver(server_url: nil, http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }) ⇒ Selenium::WebDriver

Creates a new global driver and quits the old one if it exists. You can customise http_client as the following

Examples:


require 'rubygems'
require 'appium_lib_core'

# platformName takes a string or a symbol.

# Start iOS driver
opts = {
         capabilities: {
           platformName: :ios,
           platformVersion: '11.0',
           deviceName: 'iPhone Simulator',
           automationName: 'XCUITest',
           app: '/path/to/MyiOS.app'
         },
         appium_lib: {
           wait: 20,
           wait_timeout: 20,
           wait_interval: 0.3,
         }
       }

@core = Appium::Core.for(opts) # create a core driver with 'opts' and extend methods into 'self'
@driver = @core.start_driver server_url: "http://127.0.0.1:8000/wd/hub"

# Attach custom HTTP client
@driver = @core.start_driver server_url: "http://127.0.0.1:8000/wd/hub",
                             http_client_ops: { http_client: Your:Http:Client.new,
                                                open_timeout: 1_000,
                                                read_timeout: 1_000 }

Parameters:

  • server_url (String) (defaults to: nil)

    Custom server url to send to requests. Default is “127.0.0.1:4723/wd/hub”.

  • http_client_ops (Hash) (defaults to: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })

    Options for http client

Options Hash (http_client_ops:):

  • :http_client (Hash)

    Custom HTTP Client

  • :open_timeout (Hash)

    Custom open timeout for http client.

  • :read_timeout (Hash)

    Custom read timeout for http client.

Returns:

  • (Selenium::WebDriver)

    A new driver instance



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/appium_lib_core/driver.rb', line 414

def start_driver(server_url: nil,
                 http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
  @custom_url ||= server_url || "http://127.0.0.1:#{@port}/wd/hub"

  @http_client = get_http_client http_client: http_client_ops.delete(:http_client),
                                 open_timeout: http_client_ops.delete(:open_timeout),
                                 read_timeout: http_client_ops.delete(:read_timeout)

  if @enable_idempotency_header
    if @http_client.instance_variable_defined? :@additional_headers
      @http_client.additional_headers[Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]] = SecureRandom.uuid
    else
      ::Appium::Logger.warn 'No additional_headers attribute in this http client instance'
    end
  end

  begin
    @driver = ::Appium::Core::Base::Driver.new(listener: @listener,
                                               http_client: @http_client,
                                               capabilities: @caps, # ::Appium::Core::Base::Capabilities
                                               url: @custom_url,
                                               wait_timeout: @wait_timeout,
                                               wait_interval: @wait_interval)

    if @direct_connect
      d_c = DirectConnections.new(@driver.capabilities)
      @driver.update_sending_request_to(protocol: d_c.protocol, host: d_c.host, port: d_c.port, path: d_c.path)
    end

    # export session
    write_session_id(@driver.session_id, @export_session_path) if @export_session
  rescue Errno::ECONNREFUSED
    raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
  end

  if @http_client.instance_variable_defined? :@additional_headers
    # We only need the key for a new session request. Should remove it for other following commands.
    @http_client.additional_headers.delete Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
  end

  # TODO: this method can be removed after releasing Appium 2.0, and after a while
  # since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
  # 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.
  set_automation_name_if_nil

  set_implicit_wait_by_default(@default_wait)

  @driver
end