appium_lib_core 12.0.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a805f2c5bbd5dc27e1cc04c293599e9bce82483993bbb2bbe3d52c591dad4b56
4
- data.tar.gz: '09d935617bff88069b7da75072dc322c54cf57a38de1c89da619fa60b08af9ec'
3
+ metadata.gz: cb6226306206eafec9dbd6f2c2f1ac09c86fee48ee841be8616df8acf6ce56a5
4
+ data.tar.gz: ab63b82ecafb824a8b0edfdcbcba8d9ec0e1530e8a42f86c4c95aa95246a6871
5
5
  SHA512:
6
- metadata.gz: 1d79ce647fb5484b35fd46f2433e1bc615ca72ba224136fe81710a55feae59363f1b478e5e4c5e8c1ebbf0ac11d6e1ab4352bbdfa866a7c1a5feb4a8ee7c4cee
7
- data.tar.gz: f76855bd2fd8c8f5eae448abda265b649cde89fea4998e17ec8eebb9fd4b3fcc916f095fc32a12814df41a30c6cbaaf949504840156c98f131ae3b65e8b78d09
6
+ metadata.gz: 2573d787ffb963708aae21889b3f3ab6f68b7747b623a7ba906fff4065f7514557608dfaa6b9b05b8ceddaf4a6f0ae96edb20a436af01e3148c5c13ed64bf598
7
+ data.tar.gz: 21c2e01a1bdfb8bfd81ad309eb8e6fbdfa6d5abba485c77b52c257652be9c8b58a4f62a110a78128f2f26438e0d018cebfe6f1cffd8f60e1aa91293c2d14e269
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  Read `release_notes.md` for commit level details.
4
4
 
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
+
5
11
  ## [12.0.0] - 2025-12-28
6
12
  - 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.
7
13
  - update RBS files
data/Gemfile CHANGED
@@ -8,7 +8,7 @@ gem 'minitest', '~> 5.0'
8
8
  gem 'minitest-reporters', '~> 1.1'
9
9
  gem 'parallel_tests'
10
10
  gem 'rake', '~> 13.0'
11
- gem 'rubocop', '1.82.0'
11
+ gem 'rubocop', '1.85.1'
12
12
  gem 'simplecov'
13
13
  gem 'steep', '~> 1.10.0'
14
14
  gem 'webmock', '~> 3.26.0'
@@ -47,8 +47,6 @@ module Appium
47
47
  # No 'browserName' means the session is native appium connection
48
48
  APPIUM_NATIVE_BROWSER_NAME = 'appium'
49
49
 
50
- attr_reader :available_commands
51
-
52
50
  def browser
53
51
  @browser ||= begin
54
52
  name = @capabilities&.browser_name
@@ -74,7 +72,6 @@ module Appium
74
72
  # )
75
73
  #
76
74
  def attach_to(session_id, platform_name, automation_name)
77
- @available_commands = ::Appium::Core::Commands::COMMANDS.dup
78
75
  @session_id = session_id
79
76
 
80
77
  # generate a dummy capabilities instance which only has the given platformName and automationName
@@ -109,10 +106,8 @@ module Appium
109
106
  # driver = core.start_driver
110
107
  #
111
108
  def create_session(capabilities)
112
- @available_commands = ::Appium::Core::Commands::COMMANDS.dup
113
-
114
109
  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
116
111
 
117
112
  @session_id = response['sessionId']
118
113
  raise ::Selenium::WebDriver::Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
@@ -162,29 +157,16 @@ module Appium
162
157
 
163
158
  public
164
159
 
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
184
164
  end
185
165
 
166
+ # Override Selenium's commands to resolve extra_commands from this subclass
167
+ # rather than the parent Selenium::WebDriver::Remote::Bridge.
186
168
  def commands(command)
187
- @available_commands[command] || Bridge.extra_commands[command]
169
+ command_list[command] || self.class.extra_commands&.[](command)
188
170
  end
189
171
 
190
172
  def status
@@ -273,7 +255,7 @@ module Appium
273
255
  # No implementation for W3C webdriver module
274
256
  def available_log_types
275
257
  types = execute :get_available_log_types
276
- Array(types).map(&:to_sym)
258
+ Array(types).map(&:to_sym) # steep:ignore
277
259
  end
278
260
 
279
261
  # For Appium
@@ -297,7 +279,7 @@ module Appium
297
279
 
298
280
  # For Appium
299
281
  def log_events(type = nil)
300
- args = {}
282
+ args = {} # steep:ignore
301
283
  args['type'] = type unless type.nil?
302
284
 
303
285
  execute :get_log_events, {}, args
@@ -135,25 +135,21 @@ module Appium
135
135
  # drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
136
136
  # to define custom commands that are not part of W3C spec.
137
137
  #
138
+ # Uses Selenium's +Bridge.add_command+ under the hood to register the command
139
+ # and define the method on the bridge.
140
+ #
138
141
  # @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
139
142
  # @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
140
143
  # +:session_id+ is the placeholder of 'session id'.
141
144
  # Other place holders can be specified with +:+ prefix like +:id+.
142
145
  # Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
143
146
  # @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.
154
150
  # @raise [ArgumentError] If the given +method+ is invalid value.
155
151
  #
156
- # @example
152
+ # @example Simple GET command (no block)
157
153
  #
158
154
  # @driver.add_command(
159
155
  # method: :get,
@@ -163,30 +159,37 @@ module Appium
163
159
  # # Send a GET request to 'session/<session id>/path/to/custom/url'
164
160
  # @driver.test_command
165
161
  #
162
+ # @example POST command with arguments (do/end block)
166
163
  #
167
164
  # @driver.add_command(
168
165
  # method: :post,
169
166
  # url: 'session/:session_id/path/to/custom/url',
170
167
  # 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 })
175
170
  # end
176
171
  # # Send a POST request to 'session/<session id>/path/to/custom/url'
177
172
  # # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
178
173
  # # ':session_id' in the given 'url' is replaced with current 'session id'.
179
174
  # @driver.test_command(1)
180
175
  #
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)
181
186
  #
182
187
  # @driver.add_command(
183
188
  # method: :post,
184
189
  # url: 'session/:session_id/element/:id/custom/action',
185
190
  # 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 })
190
193
  # end
191
194
  # # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
192
195
  # # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
@@ -195,10 +198,28 @@ module Appium
195
198
  # e = @driver.find_element :accessibility_id, 'an element'
196
199
  # @driver.test_action_command(e.id, 'action')
197
200
  #
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
+ #
198
211
  def add_command(method:, url:, name:, &block)
199
212
  raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}" unless AVAILABLE_METHODS.include? method
200
213
 
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)
202
223
  end
203
224
 
204
225
  ### Methods for Appium
@@ -208,7 +208,7 @@ module Appium
208
208
  chrome_send_command: [:post, 'session/:session_id/goog/cdp/execute']
209
209
  }.freeze
210
210
 
211
- COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).freeze
211
+ COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).freeze # steep:ignore
212
212
  end # module Commands
213
213
  end # module Core
214
214
  end # module Appium
@@ -18,7 +18,7 @@ module Appium
18
18
  module Device
19
19
  module AppManagement
20
20
  def app_strings(language = nil)
21
- opts = language ? { language: language } : {}
21
+ opts = language ? { language: language } : {} # steep:ignore
22
22
  execute_script 'mobile:getAppStrings', opts
23
23
  end
24
24
 
@@ -27,7 +27,7 @@ module Appium
27
27
  raise NotImplementedError
28
28
  end
29
29
 
30
- def install_app(path, options = {})
30
+ def install_app(path, options = {}) # steep:ignore
31
31
  args = { appPath: path }
32
32
  args[:options] = options unless options&.empty?
33
33
 
@@ -22,7 +22,7 @@ module Appium
22
22
  end
23
23
 
24
24
  def device_time(format = nil)
25
- arg = {}
25
+ arg = {} # steep:ignore
26
26
  arg[:format] = format unless format.nil?
27
27
 
28
28
  execute_script 'mobile:getDeviceTime', arg
@@ -18,7 +18,7 @@ module Appium
18
18
  module Device
19
19
  module DeviceLock
20
20
  def lock(duration = nil)
21
- opts = duration ? { seconds: duration } : {}
21
+ opts = duration ? { seconds: duration } : {} # steep:ignore
22
22
  execute_script 'mobile:lock', opts
23
23
  end
24
24
 
@@ -68,7 +68,7 @@ module Appium
68
68
  raise ::Appium::Core::Error::ArgumentError, "match_func should be #{MATCH_FEATURES[:match_func]}" unless MATCH_FEATURES[:match_func].member?(match_func.to_s)
69
69
  raise ::Appium::Core::Error::ArgumentError, "visualize should be #{MATCH_FEATURES[:visualize]}" unless MATCH_FEATURES[:visualize].member?(visualize)
70
70
 
71
- options = {}
71
+ options = {} # steep:ignore
72
72
  options[:detectorName] = detector_name.to_s.upcase
73
73
  options[:matchFunc] = match_func.to_s
74
74
  options[:goodMatchesFactor] = good_matches_factor.to_i unless good_matches_factor.nil?
@@ -108,7 +108,7 @@ module Appium
108
108
  "visualize should be #{MATCH_TEMPLATE[:visualize]}"
109
109
  end
110
110
 
111
- options = {}
111
+ options = {} # steep:ignore
112
112
  options[:visualize] = visualize
113
113
  options[:threshold] = threshold unless threshold.nil?
114
114
  options[:multiple] = multiple unless multiple.nil?
@@ -138,7 +138,7 @@ module Appium
138
138
  "visualize should be #{GET_SIMILARITY[:visualize]}"
139
139
  end
140
140
 
141
- options = {}
141
+ options = {} # steep:ignore
142
142
  options[:visualize] = visualize
143
143
 
144
144
  compare_images(mode: :getSimilarity, first_image: first_image, second_image: second_image, options: options)
@@ -160,7 +160,7 @@ module Appium
160
160
  def compare_images(mode: :matchFeatures, first_image:, second_image:, options: nil)
161
161
  raise ::Appium::Core::Error::ArgumentError, "content_type should be #{MODE}" unless MODE.member?(mode)
162
162
 
163
- params = {}
163
+ params = {} # steep:ignore
164
164
  params[:mode] = mode
165
165
  params[:firstImage] = Base64.strict_encode64 first_image
166
166
  params[:secondImage] = Base64.strict_encode64 second_image
@@ -18,7 +18,7 @@ module Appium
18
18
  module Device
19
19
  module Keyboard
20
20
  def hide_keyboard(close_key = nil)
21
- option = {}
21
+ option = {} # steep:ignore
22
22
 
23
23
  option[:key] = close_key || 'Done' # default to Done key.
24
24
 
@@ -35,7 +35,7 @@ module Appium
35
35
  'method should be POST or PUT'
36
36
  end
37
37
 
38
- option = {}
38
+ option = {} # steep:ignore
39
39
  option[:remotePath] = remote_path
40
40
  option[:user] = user unless user.nil?
41
41
  option[:pass] = pass unless pass.nil?
@@ -64,7 +64,7 @@ module Appium
64
64
  file_field_name: file_field_name, form_fields: form_fields, headers: headers
65
65
  ).upload_option
66
66
 
67
- params = option.empty? ? {} : { options: option }
67
+ params = option.empty? ? {} : { options: option } # steep:ignore
68
68
 
69
69
  execute(:stop_recording_screen, {}, params)
70
70
  end
@@ -603,7 +603,7 @@ module Appium
603
603
  o = opts || {}
604
604
 
605
605
  raw_caps = o[:caps] || o[:capabilities]
606
- caps_hash = raw_caps.is_a?(Hash) ? raw_caps : {}
606
+ caps_hash = raw_caps.is_a?(Hash) ? raw_caps : {} # steep:ignore
607
607
 
608
608
  Core::Base::Capabilities.new(caps_hash)
609
609
  end
@@ -631,7 +631,7 @@ module Appium
631
631
  app = get_app # for steep reason
632
632
  return unless app && app.empty?
633
633
 
634
- uri_regex = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
634
+ uri_regex = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER # steep:ignore
635
635
  return if app =~ uri_regex.make_regexp
636
636
 
637
637
  # steep:ignore
@@ -14,7 +14,7 @@
14
14
 
15
15
  module Appium
16
16
  module Core
17
- VERSION = '12.0.0' unless defined? ::Appium::Core::VERSION
18
- DATE = '2025-12-28' unless defined? ::Appium::Core::DATE
17
+ VERSION = '12.1.0' unless defined? ::Appium::Core::VERSION
18
+ DATE = '2026-03-21' unless defined? ::Appium::Core::DATE
19
19
  end
20
20
  end
@@ -36,7 +36,7 @@ module Appium
36
36
  # the methods which expect the symbol first.
37
37
  raise ::Appium::Core::Error::ArgumentError, 'symbolize_keys requires a hash' unless hash.is_a? Hash
38
38
 
39
- hash.each_with_object({}) do |pair, acc|
39
+ hash.each_with_object({}) do |pair, acc| # steep:ignore
40
40
  key = begin
41
41
  if enable_deprecation_msg && !(pair[0].is_a? Symbol)
42
42
  ::Appium::Logger.warn("[Deprecation] The key '#{pair[0]}' must be a symbol while currently it " \
@@ -4,6 +4,9 @@ module Selenium
4
4
  module UnknownError
5
5
  def message: -> String
6
6
  end
7
+
8
+ class WebDriverError < StandardError
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -10,8 +10,6 @@ module Appium
10
10
 
11
11
  @browser: untyped
12
12
 
13
- @available_commands: untyped
14
-
15
13
  @session_id: untyped
16
14
 
17
15
  # generate a dummy capabilities instance which only has the given platformName and automationName
@@ -51,8 +49,6 @@ module Appium
51
49
  # No 'browserName' means the session is native appium connection
52
50
  APPIUM_NATIVE_BROWSER_NAME: "appium"
53
51
 
54
- attr_reader available_commands: untyped
55
-
56
52
  def browser: () -> untyped
57
53
 
58
54
  # Appium only.
@@ -115,8 +111,10 @@ module Appium
115
111
 
116
112
  public
117
113
 
118
- def add_command: (method: untyped, url: untyped, name: untyped) { (?) -> untyped } -> untyped
114
+ # Override Selenium's command_list to use Appium's command definitions
115
+ def command_list: () -> Hash[Symbol, Array[Symbol | String]]
119
116
 
117
+ # Override Selenium's commands to resolve extra_commands from this subclass
120
118
  def commands: (untyped command) -> untyped
121
119
 
122
120
  def status: () -> untyped
@@ -2,6 +2,8 @@ module Appium
2
2
  module Core
3
3
  class Base
4
4
  class Capabilities < Selenium::WebDriver::Remote::Capabilities
5
+ KNOWN: Array[Symbol]
6
+
5
7
  def convert_key: (untyped key) -> untyped
6
8
 
7
9
  # Inherit json_create from parent, but you can redeclare for clarity:
@@ -157,6 +157,8 @@ module Appium
157
157
  def execute_driver: (?script: ::String, ?type: ::String, ?timeout_ms: untyped?) -> untyped
158
158
 
159
159
  def convert_to_element: (untyped response_id) -> untyped
160
+
161
+ def bidi: () -> untyped
160
162
  end
161
163
  end
162
164
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appium_lib_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.0.0
4
+ version: 12.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuaki MATSUO
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-28 00:00:00.000000000 Z
11
+ date: 2026-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver