Skip to content

Commit 3df1b2d

Browse files
authored
refactor: search contexts and its structure (#625)
* refactor: search contexts for android and ios * refactor: separate some methods * refactor: revert and separate mobile_methods * handle uiautomationName as hash * fix rubocop
1 parent 673bdef commit 3df1b2d

File tree

6 files changed

+279
-230
lines changed

6 files changed

+279
-230
lines changed

android_tests/lib/android/specs/driver.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def sauce?
3535
end
3636

3737
t 'verify Appium::Driver::Capabilities.init_caps_for_appium' do
38-
expected_app = File.absolute_path('api.apk')
38+
expected_app = File.absolute_path('../test_apps/api.apk')
3939
caps = ::Appium::Driver::Capabilities.init_caps_for_appium(platformName: 'Android',
4040
app: expected_app,
4141
appPackage: 'io.appium.android.apis',

ios_tests/lib/ios/specs/driver.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ def sauce?
5858
t 'verify all attributes' do
5959
actual = driver_attributes
6060
caps_app_for_teardown = actual[:caps][:app]
61-
expected_app = File.absolute_path('UICatalog.app')
61+
expected_app = File.absolute_path('../test_apps/UICatalog.app')
6262

63-
expected = { automation_name: 'XCUITest',
63+
expected = { automation_name: :xcuitest,
6464
custom_url: false,
6565
export_session: false,
6666
default_wait: 30,

lib/appium_lib/common/error.rb

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
module Appium
22
module Error
33
class NotSupportedAppiumServer < RuntimeError; end
4+
5+
# Server side error
6+
class ServerError; end
47
end
58
end

lib/appium_lib/driver.rb

+71-33
Original file line numberDiff line numberDiff line change
@@ -371,30 +371,11 @@ def initialize(opts = {})
371371
raise 'opts must be a hash' unless opts.is_a? Hash
372372

373373
opts = Appium.symbolize_keys opts
374-
375374
@caps = Capabilities.init_caps_for_appium(opts[:caps] || {})
376375

377376
appium_lib_opts = opts[:appium_lib] || {}
378377

379-
# appium_lib specific values
380-
@custom_url = appium_lib_opts.fetch :server_url, false
381-
@export_session = appium_lib_opts.fetch :export_session, false
382-
@default_wait = appium_lib_opts.fetch :wait, 0
383-
@sauce_username = appium_lib_opts.fetch :sauce_username, ENV['SAUCE_USERNAME']
384-
@sauce_username = nil if !@sauce_username || (@sauce_username.is_a?(String) && @sauce_username.empty?)
385-
@sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY']
386-
@sauce_access_key = nil if !@sauce_access_key || (@sauce_access_key.is_a?(String) && @sauce_access_key.empty?)
387-
@sauce_endpoint = appium_lib_opts.fetch :sauce_endpoint, ENV['SAUCE_ENDPOINT']
388-
@sauce_endpoint = 'ondemand.saucelabs.com:443/wd/hub' if
389-
!@sauce_endpoint || (@sauce_endpoint.is_a?(String) && @sauce_endpoint.empty?)
390-
@appium_port = appium_lib_opts.fetch :port, 4723
391-
# timeout and interval used in ::Appium::Comm.wait/wait_true
392-
@appium_wait_timeout = appium_lib_opts.fetch :wait_timeout, 30
393-
@appium_wait_interval = appium_lib_opts.fetch :wait_interval, 0.5
394-
395-
# to pass it in Selenium.new.
396-
# `listener = opts.delete(:listener)` is called in Selenium::Driver.new
397-
@listener = appium_lib_opts.fetch :listener, nil
378+
set_appium_lib_specific_values(appium_lib_opts)
398379

399380
# Path to the .apk, .app or .app.zip.
400381
# The path can be local or remote for Sauce.
@@ -407,6 +388,9 @@ def initialize(opts = {})
407388
@appium_device = @appium_device.is_a?(Symbol) ? @appium_device : @appium_device.downcase.strip.intern if @appium_device
408389

409390
@automation_name = @caps[:automationName] if @caps[:automationName]
391+
@automation_name = if @automation_name
392+
@automation_name.is_a?(Symbol) ? @automation_name : @automation_name.downcase.strip.intern
393+
end
410394

411395
# load common methods
412396
extend Appium::Common
@@ -416,7 +400,10 @@ def initialize(opts = {})
416400
extend Appium::Android
417401
else
418402
extend Appium::Ios
419-
extend Appium::Ios::XcuitestGesture if automation_name_is_xcuitest? # Override touch actions
403+
if automation_name_is_xcuitest? # Override touch actions
404+
extend Appium::Ios::Xcuitest
405+
extend Appium::Ios::Xcuitest::Gesture
406+
end
420407
end
421408

422409
# apply os specific patches
@@ -442,6 +429,33 @@ def initialize(opts = {})
442429
self # return newly created driver
443430
end
444431

432+
private
433+
434+
def set_appium_lib_specific_values(appium_lib_opts)
435+
@custom_url = appium_lib_opts.fetch :server_url, false
436+
@export_session = appium_lib_opts.fetch :export_session, false
437+
@default_wait = appium_lib_opts.fetch :wait, 0
438+
439+
@sauce_username = appium_lib_opts.fetch :sauce_username, ENV['SAUCE_USERNAME']
440+
@sauce_username = nil if !@sauce_username || (@sauce_username.is_a?(String) && @sauce_username.empty?)
441+
@sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY']
442+
@sauce_access_key = nil if !@sauce_access_key || (@sauce_access_key.is_a?(String) && @sauce_access_key.empty?)
443+
@sauce_endpoint = appium_lib_opts.fetch :sauce_endpoint, ENV['SAUCE_ENDPOINT']
444+
@sauce_endpoint = 'ondemand.saucelabs.com:443/wd/hub' if
445+
!@sauce_endpoint || (@sauce_endpoint.is_a?(String) && @sauce_endpoint.empty?)
446+
447+
@appium_port = appium_lib_opts.fetch :port, 4723
448+
# timeout and interval used in ::Appium::Comm.wait/wait_true
449+
@appium_wait_timeout = appium_lib_opts.fetch :wait_timeout, 30
450+
@appium_wait_interval = appium_lib_opts.fetch :wait_interval, 0.5
451+
452+
# to pass it in Selenium.new.
453+
# `listener = opts.delete(:listener)` is called in Selenium::Driver.new
454+
@listener = appium_lib_opts.fetch :listener, nil
455+
end
456+
457+
public
458+
445459
# Returns a hash of the driver attributes
446460
def driver_attributes
447461
{
@@ -469,13 +483,13 @@ def device_is_android?
469483
# Return true if automationName is 'XCUITest'
470484
# @return [Boolean]
471485
def automation_name_is_xcuitest?
472-
!@automation_name.nil? && 'xcuitest'.casecmp(@automation_name).zero?
486+
!@automation_name.nil? && @automation_name == :xcuitest
473487
end
474488

475489
# Return true if automationName is 'uiautomator2'
476490
# @return [Boolean]
477491
def automation_name_is_uiautomator2?
478-
!@automation_name.nil? && 'uiautomator2'.casecmp(@automation_name).zero?
492+
!@automation_name.nil? && @automation_name == :uiautomator2
479493
end
480494

481495
# Return true if the target Appium server is over REQUIRED_VERSION_XCUITEST.
@@ -511,11 +525,11 @@ def check_server_version_xcuitest
511525
def appium_server_version
512526
driver.remote_status
513527
rescue Selenium::WebDriver::Error::WebDriverError => ex
514-
raise unless ex.message.include?('content-type=""')
528+
raise ::Appium::Error::ServerError unless ex.message.include?('content-type=""')
515529
# server (TestObject for instance) does not respond to status call
516530
{}
517531
rescue Selenium::WebDriver::Error::ServerError => e
518-
raise unless e.message.include?('status code 500')
532+
raise ::Appium::Error::ServerError unless e.message.include?('status code 500')
519533
# driver.remote_status returns 500 error for using selenium grid
520534
{}
521535
end
@@ -611,11 +625,33 @@ def driver_quit
611625
end
612626

613627
# Creates a new global driver and quits the old one if it exists.
628+
# You can customise http_client as the following
629+
#
630+
# @example
631+
# ```ruby
632+
# require 'rubygems'
633+
# require 'appium_lib'
634+
#
635+
# # platformName takes a string or a symbol.
636+
#
637+
# # Start iOS driver
638+
# opts = {
639+
# caps: {
640+
# platformName: :ios,
641+
# app: '/path/to/MyiOS.app'
642+
# },
643+
# appium_lib: {
644+
# wait_timeout: 30
645+
# }
646+
# }
647+
# custom_http_client = Custom::Http::Client.new(opts)
648+
# Appium::Driver.new(opts).start_driver(custom_http_client)
614649
#
615650
# @return [Selenium::WebDriver] the new global driver
616-
def start_driver
651+
def start_driver(http_client =
652+
Selenium::WebDriver::Remote::Http::Default.new(open_timeout: 999_999, read_timeout: 999_999))
617653
# open_timeout and read_timeout are explicit wait.
618-
@http_client ||= Selenium::WebDriver::Remote::Http::Default.new(open_timeout: 999_999, read_timeout: 999_999)
654+
@http_client ||= http_client
619655

620656
begin
621657
driver_quit
@@ -630,12 +666,7 @@ def start_driver
630666
@driver.extend Selenium::WebDriver::DriverExtensions::HasLocation
631667

632668
# export session
633-
if @export_session
634-
# rubocop:disable Style/RescueModifier
635-
File.open('/tmp/appium_lib_session', 'w') do |f|
636-
f.puts @driver.session_id
637-
end rescue nil
638-
end
669+
write_session_id(@driver.session_id) if @export_session
639670
rescue Errno::ECONNREFUSED
640671
raise "ERROR: Unable to connect to Appium. Is the server running on #{server_url}?"
641672
end
@@ -773,6 +804,13 @@ def x
773804

774805
private
775806

807+
def write_session_id(session_id)
808+
File.open('/tmp/appium_lib_session', 'w') { |f| f.puts session_id }
809+
rescue IOError => e
810+
::Appium::Logger.warn e
811+
nil
812+
end
813+
776814
# If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
777815
# Since @automation_name is set only client side before start_driver is called.
778816
def set_automation_name_if_nil

lib/appium_lib/ios/mobile_methods.rb

+20-14
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,30 @@ class << self
1616
# find_elements :predicate, 'wdName == "Buttons"'
1717
# find_elements :predicate, 'wdValue == "SearchBar" AND isWDDivisible == 1'
1818
# ```
19-
#
20-
# @!method ios_class_chain_find
21-
# Only for XCUITest(WebDriverAgent)
22-
# find_element/s can be used with a [class chain]( https://github.com/facebook/WebDriverAgent/wiki/Queries)
23-
#
24-
# ```ruby
25-
# # select the third child button of the first child window element
26-
# find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]'
27-
# # select all the children windows
28-
# find_elements :class_chain, 'XCUIElementTypeWindow'
29-
# # select the second last child of the second child window
30-
# find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]'
31-
# ```
3219
def extended(_mod)
3320
::Appium::Driver::SearchContext::FINDERS[:uiautomation] = '-ios uiautomation'
3421
::Appium::Driver::SearchContext::FINDERS[:predicate] = '-ios predicate string'
35-
::Appium::Driver::SearchContext::FINDERS[:class_chain] = '-ios class chain'
3622
end
3723
end # class << self
24+
25+
module Xcuitest
26+
class << self
27+
# @!method ios_class_chain_find
28+
# Only for XCUITest(WebDriverAgent)
29+
# find_element/s can be used with a [class chain]( https://github.com/facebook/WebDriverAgent/wiki/Queries)
30+
#
31+
# ```ruby
32+
# # select the third child button of the first child window element
33+
# find_elements :class_chain, 'XCUIElementTypeWindow/XCUIElementTypeButton[3]'
34+
# # select all the children windows
35+
# find_elements :class_chain, 'XCUIElementTypeWindow'
36+
# # select the second last child of the second child window
37+
# find_elements :class_chain, 'XCUIElementTypeWindow[2]/XCUIElementTypeAny[-2]'
38+
# ```
39+
def extended(_mod)
40+
::Appium::Driver::SearchContext::FINDERS[:class_chain] = '-ios class chain'
41+
end
42+
end
43+
end
3844
end # module Ios
3945
end # module Appium

0 commit comments

Comments
 (0)