Skip to content

Commit 6628445

Browse files
authored
feat: implement search context and UA based on selenium 4.21 (#480)
* 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
1 parent f2ee24f commit 6628445

11 files changed

+172
-278
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Read `release_notes.md` for commit level details.
55
## [Unreleased]
66

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

910
### Bug fixes
1011

appium_lib_core.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
2222
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
2323
spec.require_paths = ['lib']
2424

25-
spec.add_runtime_dependency 'selenium-webdriver', '~> 4.2'
25+
spec.add_runtime_dependency 'selenium-webdriver', '~> 4.21'
2626
spec.add_runtime_dependency 'faye-websocket', '~> 0.11.0'
2727

2828
spec.add_development_dependency 'rake', '~> 13.0'

lib/appium_lib_core/common/base/bridge.rb

+21-75
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
module Appium
1616
module Core
1717
class Base
18+
class LocatorConverter
19+
def convert(how, what)
20+
[how, what]
21+
end
22+
end # LocatorConverter
23+
1824
class Bridge < ::Selenium::WebDriver::Remote::Bridge
1925
include Device::DeviceLock
2026
include Device::Keyboard
@@ -31,6 +37,8 @@ class Bridge < ::Selenium::WebDriver::Remote::Bridge
3137
include Device::ExecuteDriver
3238
include Device::Orientation
3339

40+
Bridge.locator_converter = LocatorConverter.new
41+
3442
# Prefix for extra capability defined by W3C
3543
APPIUM_PREFIX = 'appium:'
3644

@@ -153,6 +161,18 @@ def json_create(value)
153161
public
154162

155163
# command for Appium 2.0.
164+
165+
# Example:
166+
# driver.add_command(name: :available_contexts, method: :get, url: 'session/:session_id/contexts') do
167+
# execute(:available_contexts, {}) || []
168+
# end
169+
# Then,
170+
# driver.available_contexts #=> ["NATIVE_APP"]
171+
172+
# def add_command(method:, url:, name:, &block)
173+
# Bridge.add_command name, method, url, &block
174+
# end
175+
156176
def add_command(method:, url:, name:, &block)
157177
::Appium::Logger.info "Overriding the method '#{name}' for '#{url}'" if @available_commands.key? name
158178

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

164184
def commands(command)
165-
@available_commands[command]
185+
@available_commands[command] || Bridge.extra_commands[command]
166186
end
167187

168188
def status
@@ -216,52 +236,8 @@ def element_attribute(element, name)
216236
end
217237

218238
# For Appium
219-
# override
220-
def active_element
221-
::Appium::Core::Element.new self, element_id_from(execute(:get_active_element))
222-
end
223239
alias switch_to_active_element active_element
224240

225-
# For Appium
226-
# override
227-
def find_element_by(how, what, parent_ref = [])
228-
how, what = convert_locator(how, what)
229-
230-
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
231-
232-
parent_type, parent_id = parent_ref
233-
id = case parent_type
234-
when :element
235-
execute :find_child_element, { id: parent_id }, { using: how, value: what.to_s }
236-
when :shadow_root
237-
execute :find_shadow_child_element, { id: parent_id }, { using: how, value: what.to_s }
238-
else
239-
execute :find_element, {}, { using: how, value: what.to_s }
240-
end
241-
242-
::Appium::Core::Element.new self, element_id_from(id)
243-
end
244-
245-
# For Appium
246-
# override
247-
def find_elements_by(how, what, parent_ref = [])
248-
how, what = convert_locator(how, what)
249-
250-
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
251-
252-
parent_type, parent_id = parent_ref
253-
ids = case parent_type
254-
when :element
255-
execute :find_child_elements, { id: parent_id }, { using: how, value: what.to_s }
256-
when :shadow_root
257-
execute :find_shadow_child_elements, { id: parent_id }, { using: how, value: what.to_s }
258-
else
259-
execute :find_elements, {}, { using: how, value: what.to_s }
260-
end
261-
262-
ids.map { |id| ::Appium::Core::Element.new self, element_id_from(id) }
263-
end
264-
265241
# For Appium
266242
# @param [Hash] id The id which can get as a response from server
267243
# @return [::Appium::Core::Element]
@@ -370,36 +346,6 @@ def unwrap_script_result(arg)
370346
arg
371347
end
372348
end
373-
374-
def element_id_from(id)
375-
id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
376-
end
377-
378-
# Don't convert locators for Appium in native context
379-
def convert_locator(how, what)
380-
# case how
381-
# when 'class name'
382-
# how = 'css selector'
383-
# what = ".#{escape_css(what)}"
384-
# when 'id'
385-
# how = 'css selector'
386-
# what = "##{escape_css(what)}"
387-
# when 'name'
388-
# how = 'css selector'
389-
# what = "*[name='#{escape_css(what)}']"
390-
# when 'tag name'
391-
# how = 'css selector'
392-
# end
393-
#
394-
# if what.is_a?(Hash)
395-
# what = what.each_with_object({}) do |(h, w), hash|
396-
# h, w = convert_locator(h.to_s, w)
397-
# hash[h] = w
398-
# end
399-
# end
400-
401-
[how, what]
402-
end
403349
end # class Bridge
404350
end # class Base
405351
end # module Core

lib/appium_lib_core/common/base/driver.rb

+5-3
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ class Driver < ::Selenium::WebDriver::Driver
3232
include ::Selenium::WebDriver::DriverExtensions::HasWebStorage
3333

3434
include ::Appium::Core::Base::Rotatable
35-
include ::Appium::Core::Base::SearchContext
3635
include ::Appium::Core::Base::TakesScreenshot
3736
include ::Appium::Core::Base::HasRemoteStatus
3837
include ::Appium::Core::Base::HasLocation
3938
include ::Appium::Core::Base::HasNetworkConnection
4039

4140
include ::Appium::Core::Waitable
4241

42+
::Selenium::WebDriver::SearchContext.extra_finders = APPIUM_EXTRA_FINDERS
43+
4344
# Private API.
4445
# Do not use this for general use. Used by flutter driver to get bridge for creating a new element
4546
attr_reader :bridge
@@ -57,6 +58,7 @@ def initialize(bridge: nil, listener: nil, **opts) # rubocop:disable Lint/Missin
5758
@bidi = nil
5859

5960
# in the selenium webdriver as well
61+
::Selenium::WebDriver::Remote::Bridge.element_class = ::Appium::Core::Element
6062
bridge ||= create_bridge(**opts)
6163
add_extensions(bridge.browser)
6264
@bridge = listener ? ::Appium::Support::EventFiringBridge.new(bridge, listener, **original_opts) : bridge
@@ -1023,8 +1025,8 @@ def execute_driver(script: '', type: 'webdriverio', timeout_ms: nil)
10231025
# ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
10241026
# ele.rect #=> Can get the rect of the element
10251027
#
1026-
def convert_to_element(id)
1027-
@bridge.convert_to_element id
1028+
def convert_to_element(response_id)
1029+
@bridge.convert_to_element response_id
10281030
end
10291031
end # class Driver
10301032
end # class Base

lib/appium_lib_core/common/base/http_default.rb

+14-7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ module RequestHeaders
3131
class Default < ::Selenium::WebDriver::Remote::Http::Default
3232
attr_reader :additional_headers
3333

34+
::Selenium::WebDriver::Remote::Http::Common.user_agent = \
35+
"appium/ruby_lib_core/#{VERSION} (#{::Selenium::WebDriver::Remote::Http::Common.user_agent})"
36+
3437
# override
3538
def initialize(open_timeout: nil, read_timeout: nil)
3639
@open_timeout = open_timeout
@@ -39,6 +42,17 @@ def initialize(open_timeout: nil, read_timeout: nil)
3942
super
4043
end
4144

45+
def set_additional_header(key, value)
46+
@additional_headers[key] = value
47+
::Selenium::WebDriver::Remote::Http::Common.extra_headers = @additional_headers
48+
end
49+
50+
def delete_additional_header(key)
51+
@additional_headers.delete key
52+
::Selenium::WebDriver::Remote::Http::Common.extra_headers = @additional_headers
53+
@common_headers.delete key if defined? @common_headers
54+
end
55+
4256
# Update <code>server_url</code> provided when ruby_lib _core created a default http client.
4357
# Set <code>@http</code> as nil to re-create http client for the <code>server_url</code>
4458
#
@@ -59,13 +73,6 @@ def update_sending_request_to(scheme:, host:, port:, path:)
5973
@server_url = URI.parse "#{scheme}://#{host}:#{port}#{path}"
6074
end
6175

62-
def request(verb, url, headers, payload, redirects = 0)
63-
headers['User-Agent'] = "appium/ruby_lib_core/#{VERSION} (#{headers['User-Agent']})"
64-
headers = headers.merge @additional_headers unless @additional_headers.empty?
65-
66-
super(verb, url, headers, payload, redirects)
67-
end
68-
6976
private
7077

7178
def validate_url_param(scheme, host, port, path)

0 commit comments

Comments
 (0)