Skip to content

Commit 69ac7c4

Browse files
committed
Merge upstream/master resolving conflict by keeping symbolize_keys removal
2 parents 2e81a31 + 646f448 commit 69ac7c4

20 files changed

Lines changed: 114 additions & 78 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
All notable changes to this project will be documented in this file.
33
Read `release_notes.md` for commit level details.
44

5+
## [12.1.0] - 2026-03-21
6+
- Replace internal `add_command` method with Selnium Bridge's `add_command` to simplify the codebase.
7+
8+
## [12.0.1] - 2026-03-07
9+
- Improve Steep
10+
511
## [12.0.0] - 2025-12-28
612
- Remove `Appium::Core::WebSocket` which was for `appium/device/logcat` endpoint. Please use BiDi log endpoint such as [this test](https://github.com/appium/ruby_lib_core/blob/master/test/functional/android/webdriver/bidi_test.rb) instead.
713
- update RBS files

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ gem 'minitest', '~> 5.0'
88
gem 'minitest-reporters', '~> 1.1'
99
gem 'parallel_tests'
1010
gem 'rake', '~> 13.0'
11-
gem 'rubocop', '1.85.0'
11+
gem 'rubocop', '1.86.0'
1212
gem 'simplecov'
1313
gem 'steep', '~> 1.10.0'
1414
gem 'webmock', '~> 3.26.0'

lib/appium_lib_core/common/base/bridge.rb

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ class Bridge < ::Selenium::WebDriver::Remote::Bridge
4747
# No 'browserName' means the session is native appium connection
4848
APPIUM_NATIVE_BROWSER_NAME = 'appium'
4949

50-
attr_reader :available_commands
51-
5250
def browser
5351
@browser ||= begin
5452
name = @capabilities&.browser_name
@@ -74,7 +72,6 @@ def browser
7472
# )
7573
#
7674
def attach_to(session_id, platform_name, automation_name)
77-
@available_commands = ::Appium::Core::Commands::COMMANDS.dup
7875
@session_id = session_id
7976

8077
# generate a dummy capabilities instance which only has the given platformName and automationName
@@ -109,10 +106,8 @@ def attach_to(session_id, platform_name, automation_name)
109106
# driver = core.start_driver
110107
#
111108
def create_session(capabilities)
112-
@available_commands = ::Appium::Core::Commands::COMMANDS.dup
113-
114109
always_match = add_appium_prefix(capabilities)
115-
response = execute(:new_session, {}, { capabilities: { alwaysMatch: always_match, firstMatch: [{}] } })
110+
response = execute(:new_session, {}, { capabilities: { alwaysMatch: always_match, firstMatch: [{}] } }) # steep:ignore
116111

117112
@session_id = response['sessionId']
118113
raise ::Selenium::WebDriver::Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
@@ -162,29 +157,16 @@ def json_create(value)
162157

163158
public
164159

165-
# command for Appium 2.0.
166-
167-
# Example:
168-
# driver.add_command(name: :available_contexts, method: :get, url: 'session/:session_id/contexts') do
169-
# execute(:available_contexts, {}) || []
170-
# end
171-
# Then,
172-
# driver.available_contexts #=> ["NATIVE_APP"]
173-
174-
# def add_command(method:, url:, name:, &block)
175-
# Bridge.add_command name, method, url, &block
176-
# end
177-
178-
def add_command(method:, url:, name:, &block)
179-
::Appium::Logger.info "Overriding the method '#{name}' for '#{url}'" if @available_commands.key? name
180-
181-
@available_commands[name] = [method, url]
182-
183-
::Appium::Core::Device.add_endpoint_method name, &block
160+
# Override Selenium's command_list to use Appium's command definitions
161+
# instead of the default W3C COMMANDS from Selenium.
162+
def command_list
163+
::Appium::Core::Commands::COMMANDS
184164
end
185165

166+
# Override Selenium's commands to resolve extra_commands from this subclass
167+
# rather than the parent Selenium::WebDriver::Remote::Bridge.
186168
def commands(command)
187-
@available_commands[command] || Bridge.extra_commands[command]
169+
command_list[command] || self.class.extra_commands&.[](command)
188170
end
189171

190172
def status
@@ -273,7 +255,7 @@ def set_location(lat, lon, alt = 0.0, speed: nil, satellites: nil)
273255
# No implementation for W3C webdriver module
274256
def available_log_types
275257
types = execute :get_available_log_types
276-
Array(types).map(&:to_sym)
258+
Array(types).map(&:to_sym) # steep:ignore
277259
end
278260

279261
# For Appium
@@ -297,7 +279,7 @@ def log_event(vendor, event)
297279

298280
# For Appium
299281
def log_events(type = nil)
300-
args = {}
282+
args = {} # steep:ignore
301283
args['type'] = type unless type.nil?
302284

303285
execute :get_log_events, {}, args

lib/appium_lib_core/common/base/driver.rb

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -135,25 +135,21 @@ def update_sending_request_to(protocol:, host:, port:, path:)
135135
# drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
136136
# to define custom commands that are not part of W3C spec.
137137
#
138+
# Uses Selenium's +Bridge.add_command+ under the hood to register the command
139+
# and define the method on the bridge.
140+
#
138141
# @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
139142
# @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
140143
# +:session_id+ is the placeholder of 'session id'.
141144
# Other place holders can be specified with +:+ prefix like +:id+.
142145
# Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
143146
# @param [Symbol] name The name of method that is called as the driver instance method.
144-
# @param [Proc] block The block to involve as the method.
145-
# Please define a method that has the same +name+ with arguments you want.
146-
# The method must has +execute+ method. tHe +execute+ is calls the +url+
147-
# with the given parameters.
148-
# The first argument should be +name+ as symbol.
149-
# The second argument should be hash. If keys in the hash matches +:+ prefix
150-
# string in the given url, the matched string in the given url will be
151-
# values in the hash.
152-
# The third argument should be hash. The hash will be the request body.
153-
# Please read examples below for more details.
147+
# @param [Proc] block The block becomes the method body. It is executed in the context of
148+
# the bridge instance, so +execute+ is available. When no block is given,
149+
# a default implementation calling +execute(name)+ is used.
154150
# @raise [ArgumentError] If the given +method+ is invalid value.
155151
#
156-
# @example
152+
# @example Simple GET command (no block)
157153
#
158154
# @driver.add_command(
159155
# method: :get,
@@ -163,30 +159,37 @@ def update_sending_request_to(protocol:, host:, port:, path:)
163159
# # Send a GET request to 'session/<session id>/path/to/custom/url'
164160
# @driver.test_command
165161
#
162+
# @example POST command with arguments (do/end block)
166163
#
167164
# @driver.add_command(
168165
# method: :post,
169166
# url: 'session/:session_id/path/to/custom/url',
170167
# name: :test_command
171-
# ) do
172-
# def test_command(argument)
173-
# execute(:test_command, {}, { dummy: argument })
174-
# end
168+
# ) do |argument|
169+
# execute(:test_command, {}, { dummy: argument })
175170
# end
176171
# # Send a POST request to 'session/<session id>/path/to/custom/url'
177172
# # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
178173
# # ':session_id' in the given 'url' is replaced with current 'session id'.
179174
# @driver.test_command(1)
180175
#
176+
# @example POST command with arguments (inline block)
177+
#
178+
# @driver.add_command(
179+
# method: :post,
180+
# url: 'session/:session_id/path/to/custom/url',
181+
# name: :test_command
182+
# ) { |argument| execute(:test_command, {}, { dummy: argument }) }
183+
# @driver.test_command(1)
184+
#
185+
# @example POST command with URL placeholders (do/end block)
181186
#
182187
# @driver.add_command(
183188
# method: :post,
184189
# url: 'session/:session_id/element/:id/custom/action',
185190
# name: :test_action_command
186-
# ) do
187-
# def test_action_command(element_id, action)
188-
# execute(:test_action_command, {id: element_id}, { dummy_action: action })
189-
# end
191+
# ) do |element_id, action|
192+
# execute(:test_action_command, {id: element_id}, { dummy_action: action })
190193
# end
191194
# # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
192195
# # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
@@ -195,10 +198,28 @@ def update_sending_request_to(protocol:, host:, port:, path:)
195198
# e = @driver.find_element :accessibility_id, 'an element'
196199
# @driver.test_action_command(e.id, 'action')
197200
#
201+
# @example POST command with URL placeholders (inline block)
202+
#
203+
# @driver.add_command(
204+
# method: :post,
205+
# url: 'session/:session_id/element/:id/custom/action',
206+
# name: :test_action_command
207+
# ) { |element_id, action| execute(:test_action_command, {id: element_id}, { dummy_action: action }) }
208+
# e = @driver.find_element :accessibility_id, 'an element'
209+
# @driver.test_action_command(e.id, 'action')
210+
#
198211
def add_command(method:, url:, name:, &block)
199212
raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}" unless AVAILABLE_METHODS.include? method
200213

201-
@bridge.add_command method: method, url: url, name: name, &block
214+
::Appium::Logger.info "Overriding the command '#{name}' for '#{url}'" if Bridge.extra_commands&.key?(name)
215+
216+
# Use Selenium's Bridge.add_command to register the command and define the method.
217+
# When no block is given, create a default implementation that calls execute.
218+
block ||= proc { execute(name) }
219+
Bridge.add_command(name, method, url, &block)
220+
221+
# Ensure the driver delegates the new method to bridge
222+
self.class.class_eval { def_delegator :@bridge, name } unless self.class.method_defined?(name)
202223
end
203224

204225
### Methods for Appium

lib/appium_lib_core/common/command.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ module Commands
208208
chrome_send_command: [:post, 'session/:session_id/goog/cdp/execute']
209209
}.freeze
210210

211-
COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).freeze
211+
COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).freeze # steep:ignore
212212
end # module Commands
213213
end # module Core
214214
end # module Appium

lib/appium_lib_core/common/device/app_management.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Base
1818
module Device
1919
module AppManagement
2020
def app_strings(language = nil)
21-
opts = language ? { language: language } : {}
21+
opts = language ? { language: language } : {} # steep:ignore
2222
execute_script 'mobile:getAppStrings', opts
2323
end
2424

@@ -27,7 +27,7 @@ def background_app(duration = 0)
2727
raise NotImplementedError
2828
end
2929

30-
def install_app(path, options = {})
30+
def install_app(path, options = {}) # steep:ignore
3131
args = { appPath: path }
3232
args[:options] = options unless options&.empty?
3333

lib/appium_lib_core/common/device/device.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def shake
2222
end
2323

2424
def device_time(format = nil)
25-
arg = {}
25+
arg = {} # steep:ignore
2626
arg[:format] = format unless format.nil?
2727

2828
execute_script 'mobile:getDeviceTime', arg

lib/appium_lib_core/common/device/device_lock.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Base
1818
module Device
1919
module DeviceLock
2020
def lock(duration = nil)
21-
opts = duration ? { seconds: duration } : {}
21+
opts = duration ? { seconds: duration } : {} # steep:ignore
2222
execute_script 'mobile:lock', opts
2323
end
2424

lib/appium_lib_core/common/device/image_comparison.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def match_images_features(first_image:,
6868
raise ::Appium::Core::Error::ArgumentError, "match_func should be #{MATCH_FEATURES[:match_func]}" unless MATCH_FEATURES[:match_func].member?(match_func.to_s)
6969
raise ::Appium::Core::Error::ArgumentError, "visualize should be #{MATCH_FEATURES[:visualize]}" unless MATCH_FEATURES[:visualize].member?(visualize)
7070

71-
options = {}
71+
options = {} # steep:ignore
7272
options[:detectorName] = detector_name.to_s.upcase
7373
options[:matchFunc] = match_func.to_s
7474
options[:goodMatchesFactor] = good_matches_factor.to_i unless good_matches_factor.nil?
@@ -108,7 +108,7 @@ def find_image_occurrence(full_image:, partial_image:, visualize: false, thresho
108108
"visualize should be #{MATCH_TEMPLATE[:visualize]}"
109109
end
110110

111-
options = {}
111+
options = {} # steep:ignore
112112
options[:visualize] = visualize
113113
options[:threshold] = threshold unless threshold.nil?
114114
options[:multiple] = multiple unless multiple.nil?
@@ -138,7 +138,7 @@ def get_images_similarity(first_image:, second_image:, visualize: false)
138138
"visualize should be #{GET_SIMILARITY[:visualize]}"
139139
end
140140

141-
options = {}
141+
options = {} # steep:ignore
142142
options[:visualize] = visualize
143143

144144
compare_images(mode: :getSimilarity, first_image: first_image, second_image: second_image, options: options)
@@ -160,7 +160,7 @@ def get_images_similarity(first_image:, second_image:, visualize: false)
160160
def compare_images(mode: :matchFeatures, first_image:, second_image:, options: nil)
161161
raise ::Appium::Core::Error::ArgumentError, "content_type should be #{MODE}" unless MODE.member?(mode)
162162

163-
params = {}
163+
params = {} # steep:ignore
164164
params[:mode] = mode
165165
params[:firstImage] = Base64.strict_encode64 first_image
166166
params[:secondImage] = Base64.strict_encode64 second_image

lib/appium_lib_core/common/device/keyboard.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Base
1818
module Device
1919
module Keyboard
2020
def hide_keyboard(close_key = nil)
21-
option = {}
21+
option = {} # steep:ignore
2222

2323
option[:key] = close_key || 'Done' # default to Done key.
2424

0 commit comments

Comments
 (0)