Skip to content

Commit

Permalink
feat: implement search context and UA based on selenium 4.21 (#480)
Browse files Browse the repository at this point in the history
* set the deps destination [skip ci]

* use github

* mofidy to use extra finders

* fix lint

* add ua

* revert unnecessary naming changes

* add new line

* revert docstring

* simplify a bit

* remove request

* add example of add_command

* chore: update to the latest imple

* set element with ::Selenium::WebDriver::Remote::Bridge.element_class and add tests

* fix lint

* removed unnecessary override

* Update Gemfile

* add bundle exec

* remove completed todo

* use selenium webdriver 4.21

* update changelog
  • Loading branch information
KazuCocoa authored May 17, 2024
1 parent f2ee24f commit 6628445
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 278 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Read `release_notes.md` for commit level details.
## [Unreleased]

### Enhancements
- Simplify internal code with Selenium 4.21.0. Now it requires selenium webdriver v4.21.0.

### Bug fixes

Expand Down
2 changes: 1 addition & 1 deletion appium_lib_core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_runtime_dependency 'selenium-webdriver', '~> 4.2'
spec.add_runtime_dependency 'selenium-webdriver', '~> 4.21'
spec.add_runtime_dependency 'faye-websocket', '~> 0.11.0'

spec.add_development_dependency 'rake', '~> 13.0'
Expand Down
96 changes: 21 additions & 75 deletions lib/appium_lib_core/common/base/bridge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
module Appium
module Core
class Base
class LocatorConverter
def convert(how, what)
[how, what]
end
end # LocatorConverter

class Bridge < ::Selenium::WebDriver::Remote::Bridge
include Device::DeviceLock
include Device::Keyboard
Expand All @@ -31,6 +37,8 @@ class Bridge < ::Selenium::WebDriver::Remote::Bridge
include Device::ExecuteDriver
include Device::Orientation

Bridge.locator_converter = LocatorConverter.new

# Prefix for extra capability defined by W3C
APPIUM_PREFIX = 'appium:'

Expand Down Expand Up @@ -153,6 +161,18 @@ def json_create(value)
public

# command for Appium 2.0.

# Example:
# driver.add_command(name: :available_contexts, method: :get, url: 'session/:session_id/contexts') do
# execute(:available_contexts, {}) || []
# end
# Then,
# driver.available_contexts #=> ["NATIVE_APP"]

# def add_command(method:, url:, name:, &block)
# Bridge.add_command name, method, url, &block
# end

def add_command(method:, url:, name:, &block)
::Appium::Logger.info "Overriding the method '#{name}' for '#{url}'" if @available_commands.key? name

Expand All @@ -162,7 +182,7 @@ def add_command(method:, url:, name:, &block)
end

def commands(command)
@available_commands[command]
@available_commands[command] || Bridge.extra_commands[command]
end

def status
Expand Down Expand Up @@ -216,52 +236,8 @@ def element_attribute(element, name)
end

# For Appium
# override
def active_element
::Appium::Core::Element.new self, element_id_from(execute(:get_active_element))
end
alias switch_to_active_element active_element

# For Appium
# override
def find_element_by(how, what, parent_ref = [])
how, what = convert_locator(how, what)

return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'

parent_type, parent_id = parent_ref
id = case parent_type
when :element
execute :find_child_element, { id: parent_id }, { using: how, value: what.to_s }
when :shadow_root
execute :find_shadow_child_element, { id: parent_id }, { using: how, value: what.to_s }
else
execute :find_element, {}, { using: how, value: what.to_s }
end

::Appium::Core::Element.new self, element_id_from(id)
end

# For Appium
# override
def find_elements_by(how, what, parent_ref = [])
how, what = convert_locator(how, what)

return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'

parent_type, parent_id = parent_ref
ids = case parent_type
when :element
execute :find_child_elements, { id: parent_id }, { using: how, value: what.to_s }
when :shadow_root
execute :find_shadow_child_elements, { id: parent_id }, { using: how, value: what.to_s }
else
execute :find_elements, {}, { using: how, value: what.to_s }
end

ids.map { |id| ::Appium::Core::Element.new self, element_id_from(id) }
end

# For Appium
# @param [Hash] id The id which can get as a response from server
# @return [::Appium::Core::Element]
Expand Down Expand Up @@ -370,36 +346,6 @@ def unwrap_script_result(arg)
arg
end
end

def element_id_from(id)
id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
end

# Don't convert locators for Appium in native context
def convert_locator(how, what)
# case how
# when 'class name'
# how = 'css selector'
# what = ".#{escape_css(what)}"
# when 'id'
# how = 'css selector'
# what = "##{escape_css(what)}"
# when 'name'
# how = 'css selector'
# what = "*[name='#{escape_css(what)}']"
# when 'tag name'
# how = 'css selector'
# end
#
# if what.is_a?(Hash)
# what = what.each_with_object({}) do |(h, w), hash|
# h, w = convert_locator(h.to_s, w)
# hash[h] = w
# end
# end

[how, what]
end
end # class Bridge
end # class Base
end # module Core
Expand Down
8 changes: 5 additions & 3 deletions lib/appium_lib_core/common/base/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ class Driver < ::Selenium::WebDriver::Driver
include ::Selenium::WebDriver::DriverExtensions::HasWebStorage

include ::Appium::Core::Base::Rotatable
include ::Appium::Core::Base::SearchContext
include ::Appium::Core::Base::TakesScreenshot
include ::Appium::Core::Base::HasRemoteStatus
include ::Appium::Core::Base::HasLocation
include ::Appium::Core::Base::HasNetworkConnection

include ::Appium::Core::Waitable

::Selenium::WebDriver::SearchContext.extra_finders = APPIUM_EXTRA_FINDERS

# Private API.
# Do not use this for general use. Used by flutter driver to get bridge for creating a new element
attr_reader :bridge
Expand All @@ -57,6 +58,7 @@ def initialize(bridge: nil, listener: nil, **opts) # rubocop:disable Lint/Missin
@bidi = nil

# in the selenium webdriver as well
::Selenium::WebDriver::Remote::Bridge.element_class = ::Appium::Core::Element
bridge ||= create_bridge(**opts)
add_extensions(bridge.browser)
@bridge = listener ? ::Appium::Support::EventFiringBridge.new(bridge, listener, **original_opts) : bridge
Expand Down Expand Up @@ -1023,8 +1025,8 @@ def execute_driver(script: '', type: 'webdriverio', timeout_ms: nil)
# ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
# ele.rect #=> Can get the rect of the element
#
def convert_to_element(id)
@bridge.convert_to_element id
def convert_to_element(response_id)
@bridge.convert_to_element response_id
end
end # class Driver
end # class Base
Expand Down
21 changes: 14 additions & 7 deletions lib/appium_lib_core/common/base/http_default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ module RequestHeaders
class Default < ::Selenium::WebDriver::Remote::Http::Default
attr_reader :additional_headers

::Selenium::WebDriver::Remote::Http::Common.user_agent = \
"appium/ruby_lib_core/#{VERSION} (#{::Selenium::WebDriver::Remote::Http::Common.user_agent})"

# override
def initialize(open_timeout: nil, read_timeout: nil)
@open_timeout = open_timeout
Expand All @@ -39,6 +42,17 @@ def initialize(open_timeout: nil, read_timeout: nil)
super
end

def set_additional_header(key, value)
@additional_headers[key] = value
::Selenium::WebDriver::Remote::Http::Common.extra_headers = @additional_headers
end

def delete_additional_header(key)
@additional_headers.delete key
::Selenium::WebDriver::Remote::Http::Common.extra_headers = @additional_headers
@common_headers.delete key if defined? @common_headers
end

# Update <code>server_url</code> provided when ruby_lib _core created a default http client.
# Set <code>@http</code> as nil to re-create http client for the <code>server_url</code>
#
Expand All @@ -59,13 +73,6 @@ def update_sending_request_to(scheme:, host:, port:, path:)
@server_url = URI.parse "#{scheme}://#{host}:#{port}#{path}"
end

def request(verb, url, headers, payload, redirects = 0)
headers['User-Agent'] = "appium/ruby_lib_core/#{VERSION} (#{headers['User-Agent']})"
headers = headers.merge @additional_headers unless @additional_headers.empty?

super(verb, url, headers, payload, redirects)
end

private

def validate_url_param(scheme, host, port, path)
Expand Down
Loading

0 comments on commit 6628445

Please sign in to comment.