appium_lib 10.4.0 → 11.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,7 +27,7 @@ module Appium
27
27
  @filter = value.to_s.downcase
28
28
  end
29
29
 
30
- def initialize
30
+ def initialize # rubocop:disable Lint/MissingSuper
31
31
  reset
32
32
  @filter = false
33
33
  end
@@ -217,7 +217,7 @@ module Appium
217
217
  # [^\/]+ - type is made up of at least one non-/ characters
218
218
  # \\/ - / ends the type and starts the name
219
219
  # [\S]+$ - the name contains at least one non-space character and then the line is ended
220
- resource_id = /^[a-zA-Z_][a-zA-Z0-9\._]*:[^\/]+\/[\S]+$/
220
+ resource_id = /^[a-zA-Z_][a-zA-Z0-9._]*:[^\/]+\/\S+$/
221
221
  string.match(resource_id) ? on_match : ''
222
222
  end
223
223
 
@@ -60,9 +60,9 @@ module Appium
60
60
  # uiautomator index doesn't support last
61
61
  # and it's 0 indexed
62
62
  button_index = tags(BUTTON).length
63
- button_index -= 1 if button_index > 0
63
+ button_index -= 1 if button_index.positive?
64
64
  image_button_index = tags(IMAGE_BUTTON).length
65
- image_button_index -= 1 if image_button_index > 0
65
+ image_button_index -= 1 if image_button_index.positive?
66
66
 
67
67
  find_element :uiautomator,
68
68
  _button_visible_selectors(button_index: button_index,
@@ -63,9 +63,9 @@ module Appium
63
63
  # uiautomator index doesn't support last
64
64
  # and it's 0 indexed
65
65
  button_index = tags(::Appium::Android::BUTTON).length
66
- button_index -= 1 if button_index > 0
66
+ button_index -= 1 if button_index.positive?
67
67
  image_button_index = tags(::Appium::Android::IMAGE_BUTTON).length
68
- image_button_index -= 1 if image_button_index > 0
68
+ image_button_index -= 1 if image_button_index.positive?
69
69
 
70
70
  _button_visible_selectors_xpath(button_index: button_index,
71
71
  image_button_index: image_button_index)
@@ -60,9 +60,9 @@ module Appium
60
60
  # uiautomator index doesn't support last
61
61
  # and it's 0 indexed
62
62
  button_index = tags(::Appium::Android::Button).length
63
- button_index -= 1 if button_index > 0
63
+ button_index -= 1 if button_index.positive?
64
64
  image_button_index = tags(::Appium::Android::ImageButton).length
65
- image_button_index -= 1 if image_button_index > 0
65
+ image_button_index -= 1 if image_button_index.positive?
66
66
 
67
67
  elements = find_elements :uiautomator,
68
68
  _button_visible_selectors(button_index: button_index,
@@ -77,9 +77,8 @@ module Appium
77
77
  Appium::Logger.info "Loading #{toml}" if verbose
78
78
 
79
79
  data = Tomlrb.load_file(toml, symbolize_keys: true)
80
- if verbose
81
- Appium::Logger.info data unless data.empty?
82
- end
80
+
81
+ Appium::Logger.info data if verbose && !data.empty?
83
82
 
84
83
  if data && data[:caps] && data[:caps][:app] && !data[:caps][:app].empty?
85
84
  data[:caps][:app] = Appium::Driver.absolute_app_path data
@@ -200,12 +199,17 @@ module Appium
200
199
  raise 'Driver is nil' if driver.nil?
201
200
 
202
201
  # Wrap single class into an array
203
- class_array = [class_array] unless class_array.class == Array
202
+ class_array = [class_array] unless class_array.instance_of? Array
204
203
  # Promote Appium driver methods to class instance methods.
205
204
  class_array.each do |klass|
206
- driver.public_methods(false).each do |m|
205
+ driver.public_methods(false).each do |method|
207
206
  klass.class_eval do
208
- define_method m do |*args, &block|
207
+ if method_defined? method
208
+ ::Appium::Logger.warn "'#{method}' is already defined. Skipping to override it."
209
+ next
210
+ end
211
+
212
+ define_method method do |*args, &block|
209
213
  begin
210
214
  # Prefer existing method.
211
215
  # super will invoke method missing on driver
@@ -215,7 +219,14 @@ module Appium
215
219
  # so rescue argument error
216
220
  # and call the name method on $driver
217
221
  rescue NoMethodError, ArgumentError
218
- driver.send m, *args, &block if driver.respond_to?(m)
222
+ if args.size == 1 && args.first.is_a?(Hash)
223
+ # To prevent warnings by keyword arguments (for Ruby 2.7 and 3)
224
+ driver.send method, **args.first, &block if driver.respond_to?(method)
225
+ else
226
+ ::Appium::Logger.warn "Should fix this '#{args}' for Ruby 2.7 (and 3)" if args.first.is_a?(Hash)
227
+
228
+ driver.send method, *args, &block if driver.respond_to?(method)
229
+ end
219
230
  end
220
231
  end
221
232
  end
@@ -29,7 +29,8 @@ module Appium
29
29
  # Return yield and ignore any exceptions.
30
30
  def ignore
31
31
  yield
32
- rescue Exception # rubocop:disable Lint/HandleExceptions, Lint/RescueException
32
+ rescue Exception # rubocop:disable Lint/RescueException
33
+ # Ignored
33
34
  end
34
35
 
35
36
  # Navigate back.
@@ -73,7 +74,7 @@ module Appium
73
74
  class CountElements < Nokogiri::XML::SAX::Document
74
75
  attr_reader :result
75
76
 
76
- def initialize(platform)
77
+ def initialize(platform) # rubocop:disable Lint/MissingSuper
77
78
  reset
78
79
  @platform = platform
79
80
  end
@@ -213,7 +214,7 @@ module Appium
213
214
  @filter = value.to_s.downcase
214
215
  end
215
216
 
216
- def initialize
217
+ def initialize # rubocop:disable Lint/MissingSuper
217
218
  reset
218
219
  @filter = false
219
220
  end
@@ -232,8 +233,9 @@ module Appium
232
233
  "#{string} #{attr[0]}: #{attr1}\n"
233
234
  end
234
235
 
235
- r.concat "\n#{name}\n#{attr_string}" unless attr_string.nil? || attr_string.empty?
236
- r
236
+ return r if attr_string.nil? || attr_string.empty?
237
+
238
+ "#{r}\n#{name}\n#{attr_string}"
237
239
  end
238
240
  end
239
241
 
@@ -45,7 +45,7 @@ module Appium
45
45
  opts = opts.is_a?(Numeric) ? { timeout: opts } : opts
46
46
 
47
47
  if opts.is_a? Hash
48
- opts.empty? ? @core.wait_true { yield } : @core.wait_true(opts) { yield }
48
+ opts.empty? ? @core.wait_true { yield } : @core.wait_true(**opts) { yield }
49
49
  else
50
50
  ::Appium::Logger.warn('Arguments should be Hash like {timeout: 100}')
51
51
  end
@@ -74,7 +74,7 @@ module Appium
74
74
  opts = opts.is_a?(Numeric) ? { timeout: opts } : opts
75
75
 
76
76
  if opts.is_a? Hash
77
- opts.empty? ? @core.wait { yield } : @core.wait(opts) { yield }
77
+ opts.empty? ? @core.wait { yield } : @core.wait(**opts) { yield }
78
78
  else
79
79
  ::Appium::Logger.warn('Arguments should be Hash like {timeout: 100}')
80
80
  end
@@ -163,9 +163,8 @@ module Appium
163
163
  global_driver = true # if global_driver is nil, then global_driver must be default value.
164
164
  end
165
165
 
166
- if global_driver
167
- $driver.driver_quit if $driver
168
- end
166
+ $driver&.driver_quit if global_driver
167
+
169
168
  raise 'opts must be a hash' unless opts.is_a? Hash
170
169
 
171
170
  @core = ::Appium::Core.for(opts)
@@ -253,7 +252,7 @@ module Appium
253
252
  # https://github.com/YOU-i-Labs/appium-youiengine-driver
254
253
  Appium::Logger.debug('YouiEngine')
255
254
  else
256
- Appium::Logger.debug('no device matched') # core also shows waring message
255
+ Appium::Logger.debug('no device matched') # core also shows warning message
257
256
  end
258
257
  end
259
258
  end
@@ -277,7 +276,6 @@ module Appium
277
276
 
278
277
  # Returns a hash of the driver attributes
279
278
  def driver_attributes
280
- # rubocop:disable Layout/AlignHash
281
279
  {
282
280
  caps: @core.caps,
283
281
  automation_name: @core.automation_name,
@@ -295,7 +293,6 @@ module Appium
295
293
  wait_timeout: @core.wait_timeout,
296
294
  wait_interval: @core.wait_interval
297
295
  }
298
- # rubocop:enable Layout/AlignHash
299
296
  end
300
297
 
301
298
  def device_is_android?
@@ -426,14 +423,12 @@ module Appium
426
423
  return app_path if app_path =~ URI::DEFAULT_PARSER.make_regexp # public URL for Sauce
427
424
 
428
425
  absolute_app_path = File.expand_path app_path
429
- app_path = if File.exist? absolute_app_path
430
- absolute_app_path
431
- else
432
- ::Appium::Logger.info("Use #{app_path}")
433
- app_path
434
- end
435
-
436
- app_path
426
+ if File.exist? absolute_app_path
427
+ absolute_app_path
428
+ else
429
+ ::Appium::Logger.info("Use #{app_path}")
430
+ app_path
431
+ end
437
432
  end
438
433
 
439
434
  # Get the server url
@@ -798,6 +793,48 @@ module Appium
798
793
  @driver.set_location(latitude, longitude, altitude)
799
794
  end
800
795
 
796
+ # @since Appium 1.16.0
797
+ #
798
+ # Logs a custom event. The event is available via {::Appium::Core::Events#get} or
799
+ # <code>driver.session_capabilities['events']</code> with <code>eventTimings</code> capabilities.
800
+ #
801
+ # @param [String] vendor The vendor prefix for the event
802
+ # @param [String] event The name of event
803
+ # @return [nil]
804
+ #
805
+ # @example
806
+ #
807
+ # log_event vendor: 'appium', event: 'funEvent'
808
+ #
809
+ # log_event = { vendor: 'appium', event: 'anotherEvent' }
810
+ # log_events #=> {...., 'appium:funEvent' => [1572957315, 1572960305],
811
+ # # 'appium:anotherEvent' => 1572959315}
812
+ #
813
+ def log_event(vendor:, event:)
814
+ @driver.logs.event vendor: vendor, event: event
815
+ end
816
+
817
+ def log_event=(log_event)
818
+ raise ArgumentError('log_event should be Hash like { vendor: "appium", event: "funEvent"}') unless log_event.is_a?(Hash)
819
+
820
+ @driver.logs.event vendor: log_event[:vendor], event: log_event[:event]
821
+ end
822
+
823
+ # @since Appium 1.16.0
824
+ # Returns events with filtering with 'type'. Defaults to all available events.
825
+ #
826
+ # @param [String] type The type of events to get
827
+ # @return [Hash]
828
+ #
829
+ # @example
830
+ #
831
+ # log_events #=> {}
832
+ # log_events #=> {'commands' => [{'cmd' => 123455, ....}], 'startTime' => 1572954894127, }
833
+ #
834
+ def log_events(type = nil)
835
+ @driver.logs.events(type)
836
+ end
837
+
801
838
  # Quit the driver and Pry.
802
839
  # quit and exit are reserved by Pry.
803
840
  # @return [void]
@@ -807,11 +844,3 @@ module Appium
807
844
  end
808
845
  end # class Driver
809
846
  end # module Appium
810
-
811
- # Paging in Pry is annoying :q required to exit.
812
- # With pager disabled, the output is similar to IRB
813
- # Only set if Pry is defined and there is no `.pryrc` files.
814
- if defined?(Pry) && !(File.exist?(Pry::HOME_RC_FILE) || File.exist?(Pry::LOCAL_RC_FILE))
815
- Appium::Logger.debug 'Pry.config.pager = false is set.'
816
- Pry.config.pager = false
817
- end
@@ -21,7 +21,7 @@ module Appium
21
21
  def start_element(type, attrs = [])
22
22
  return if filter && !filter.eql?(type)
23
23
 
24
- page = attrs.each_with_object({}) do |hash, attr|
24
+ page = attrs.each_with_object({}) do |attr, hash|
25
25
  hash[attr[0]] = attr[1] if %w(name label value hint visible).include?(attr[0])
26
26
  hash
27
27
  end
@@ -49,6 +49,7 @@ module Appium
49
49
  puts " visible: #{visible}" if visible
50
50
  end
51
51
  end
52
+
52
53
  # iOS only. On Android uiautomator always returns an empty string for EditText password.
53
54
  #
54
55
  # Password character returned from value of UIASecureTextField
@@ -76,7 +77,7 @@ module Appium
76
77
  source = get_source
77
78
 
78
79
  # current_context may be nil which breaks start_with
79
- if current_context && current_context.start_with?('WEBVIEW')
80
+ if current_context&.start_with?('WEBVIEW')
80
81
  parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Appium::Common::HTMLElements.new)
81
82
  parser.document.reset
82
83
  parser.document.filter = class_name
@@ -470,7 +471,7 @@ module Appium
470
471
  # will be present.
471
472
  _validate_object opts[:name], opts[:label], opts[:value]
472
473
 
473
- # note that mainWindow is sometimes nil so it's passed as a param
474
+ # NOTE: that mainWindow is sometimes nil so it's passed as a param
474
475
  # $._elementOrElementsByType will validate that the window isn't nil
475
476
  element_or_elements_by_type = <<-JS
476
477
  (function() {
@@ -15,7 +15,6 @@
15
15
  require_relative 'xcuitest/element'
16
16
  require_relative 'xcuitest/command'
17
17
  require_relative 'xcuitest/helper'
18
- require_relative 'xcuitest/element'
19
18
  require_relative 'xcuitest/bridge'
20
19
 
21
20
  module Appium
@@ -79,7 +79,7 @@ module Appium
79
79
  # double_tap x: 100, y: 100
80
80
  # double_tap element: find_element(:accessibility_id, "some item")
81
81
  # ```
82
- def double_tap(x: nil, y: nil, element: nil) # rubocop:disable Naming/UncommunicativeMethodParamName
82
+ def double_tap(x: nil, y: nil, element: nil)
83
83
  return 'Set x, y or element' if (x.nil? || y.nil?) && element.nil?
84
84
 
85
85
  args = element.nil? ? { x: x, y: y } : { element: element.ref }
@@ -96,7 +96,7 @@ module Appium
96
96
  # touch_and_hold x: 100, y: 100, duration: 2.0
97
97
  # touch_and_hold element: find_element(:accessibility_id, "some item")
98
98
  # ```
99
- def touch_and_hold(x: nil, y: nil, element: nil, duration: 1.0) # rubocop:disable Naming/UncommunicativeMethodParamName
99
+ def touch_and_hold(x: nil, y: nil, element: nil, duration: 1.0)
100
100
  return 'Set x, y or element' if (x.nil? || y.nil?) && element.nil?
101
101
 
102
102
  args = element.nil? ? { x: x, y: y } : { element: element.ref }
@@ -121,10 +121,10 @@ module Appium
121
121
  # Otherwise they should be calculated relatively to screen borders.
122
122
  #
123
123
  # ```ruby
124
- # tap x: 100, y: 100
125
- # tap x: 100, y: 100, element: find_element(:accessibility_id, "some item")
124
+ # one_finger_tap x: 100, y: 100
125
+ # one_finger_tap x: 100, y: 100, element: find_element(:accessibility_id, "some item")
126
126
  # ```
127
- def tap(x:, y:, element: nil) # rubocop:disable Naming/UncommunicativeMethodParamName
127
+ def one_finger_tap(x:, y:, element: nil)
128
128
  args = { x: x, y: y }
129
129
  args[:element] = element.ref if element
130
130
  @driver.execute_script 'mobile: tap', args
@@ -33,8 +33,7 @@ module Appium
33
33
  # xcuitest_install_app(app: "path/to/app.app")
34
34
  #
35
35
  def xcuitest_install_app(app:)
36
- args = { app: app }
37
- @driver.execute_script 'mobile: installApp', args
36
+ @driver.install_app app
38
37
  end
39
38
 
40
39
  # Verifies whether the application with given bundle identifier is installed on the device.
@@ -47,8 +46,7 @@ module Appium
47
46
  # xcuitest_app_installed?(bundle_id: "io.appium.bundle") #=> true or false
48
47
  #
49
48
  def xcuitest_app_installed?(bundle_id:)
50
- args = { bundleId: bundle_id }
51
- @driver.execute_script 'mobile: isAppInstalled', args
49
+ @driver.app_installed? bundle_id
52
50
  end
53
51
 
54
52
  # Uninstalls an existing application from the device under test. This endpoint does not verify
@@ -62,8 +60,7 @@ module Appium
62
60
  # xcuitest_remove_app(bundle_id: "io.appium.bundle") #=> 1
63
61
  #
64
62
  def xcuitest_remove_app(bundle_id:)
65
- args = { bundleId: bundle_id }
66
- @driver.execute_script 'mobile: removeApp', args
63
+ @driver.remove_app bundle_id
67
64
  end
68
65
 
69
66
  # Executes an existing application on the device. If the application is already running then
@@ -92,8 +89,7 @@ module Appium
92
89
  # xcuitest_terminate_app(bundle_id: "io.appium.bundle") #=> 1
93
90
  #
94
91
  def xcuitest_terminate_app(bundle_id:)
95
- args = { bundleId: bundle_id }
96
- @driver.execute_script 'mobile: terminateApp', args
92
+ @driver.terminate_app bundle_id
97
93
  end
98
94
 
99
95
  # Get the status of an existing application on the device.
@@ -114,8 +110,7 @@ module Appium
114
110
  # xcuitest_query_app_status(bundle_id: "io.appium.bundle") #=> 1
115
111
  #
116
112
  def xcuitest_query_app_status(bundle_id:)
117
- args = { bundleId: bundle_id }
118
- @driver.execute_script 'mobile: queryAppState', args
113
+ @driver.app_state bundle_id
119
114
  end
120
115
 
121
116
  # Activates an existing application on the device under test and moves it to the foreground.
@@ -130,8 +125,7 @@ module Appium
130
125
  # xcuitest_activate_app(bundle_id: "io.appium.bundle") #=> 1
131
126
  #
132
127
  def xcuitest_activate_app(bundle_id:)
133
- args = { bundleId: bundle_id }
134
- @driver.execute_script 'mobile: activateApp', args
128
+ @driver.activate_app bundle_id
135
129
  end
136
130
  end
137
131
  end # module Xcuitest
@@ -74,7 +74,7 @@ module Appium
74
74
  c_names = class_names.map { |class_name| %(type == "#{class_name}") }.join(' || ')
75
75
 
76
76
  predicate = if value
77
- %((#{c_names}) && (name contains[c] "#{value}" || label contains[c] "#{value}" || value contains[c] "#{value}")) # rubocop:disable Metrics/LineLength
77
+ %((#{c_names}) && (name contains[c] "#{value}" || label contains[c] "#{value}" || value contains[c] "#{value}")) # rubocop:disable Layout/LineLength
78
78
  else
79
79
  c_names
80
80
  end
@@ -14,6 +14,6 @@
14
14
 
15
15
  module Appium
16
16
  # Version and Date are defined on the 'Appium' module, not 'Appium::Common'
17
- VERSION = '10.4.0' unless defined? ::Appium::VERSION
18
- DATE = '2019-07-19' unless defined? ::Appium::DATE
17
+ VERSION = '11.1.0' unless defined? ::Appium::VERSION
18
+ DATE = '2020-12-29' unless defined? ::Appium::DATE
19
19
  end
data/readme.md CHANGED
@@ -21,7 +21,7 @@ We can avoid the class driver with current `ruby_lib`, but if you'd like to impl
21
21
  # Setup
22
22
  ## Requirement
23
23
  - [Appium](https://github.com/appium/appium#requirements)
24
- - Ruby: 2.2+
24
+ - Ruby: 2.4+
25
25
 
26
26
  ### Ruby Lib and Appium
27
27
  - Ruby library version over `9.8.0` requires Appium over `1.8`
@@ -1,3 +1,46 @@
1
+ #### v11.1.0 2020-12-29
2
+
3
+ - [bc3da9b](https://github.com/appium/ruby_lib/commit/bc3da9bc1ee58d5e96811b72f3ed737ed0e9c401) Release 11.1.0
4
+ - [3c33cd6](https://github.com/appium/ruby_lib/commit/3c33cd639f8ce55fb81c5ae2116a863407529599) ci: Set up CI with Azure Pipelines (#895)
5
+ - [ecee089](https://github.com/appium/ruby_lib/commit/ecee08981f935295e6413cd98dea4366cbc7bc69) feat: work with Ruby 3 (#893)
6
+ - [bb3d715](https://github.com/appium/ruby_lib/commit/bb3d715e39509ac091b8f493cca42eb4fe8fd5bf) feat: work with Ruby 3 (#892)
7
+ - [eb5cee7](https://github.com/appium/ruby_lib/commit/eb5cee7a926b90bbedd6aa28f013a0b3c5f38510) chore: Update rubocop requirement from = 1.6.1 to = 1.7.0 (#891)
8
+ - [219181d](https://github.com/appium/ruby_lib/commit/219181d80f3b86999706f90a68996945fdc7b1d0) fix: rubocop (#890)
9
+ - [334b791](https://github.com/appium/ruby_lib/commit/334b791a3c4420598e3e832023aa1a16af92492f) chore: Update fakefs requirement from ~> 0.13.0 to ~> 1.3.0 (#888)
10
+
11
+
12
+ #### v11.0.0 2020-12-19
13
+
14
+ - [b4313b0](https://github.com/appium/ruby_lib/commit/b4313b09e62dd22bb95c2e0bc6edd296c7ece7b4) Release 11.0.0
15
+ - [42f01dd](https://github.com/appium/ruby_lib/commit/42f01dd3513e82f9a7ed25ac9055a65dfaa7d81f) feat: bump ruby version (#887)
16
+ - [dc228b3](https://github.com/appium/ruby_lib/commit/dc228b33487281d0428dcd20e92263d7132dd53e) ci: move to GitHub Actions (#882)
17
+
18
+
19
+ #### v10.6.0 2020-04-03
20
+
21
+ - [65f690f](https://github.com/appium/ruby_lib/commit/65f690f1fec62a5f8868270287b0099d8b22a2ac) Release 10.6.0
22
+ - [f792db3](https://github.com/appium/ruby_lib/commit/f792db3afbfe3f6056e0af60ce9f6be3e2cecadc) feat: remove pager off (#879)
23
+ - [7ad3189](https://github.com/appium/ruby_lib/commit/7ad318925c0cd2c3e73cc015a1ec929485b8af85) Update hashdiff requirement from ~> 0.3.7 to ~> 1.0.0 (#874)
24
+ - [457e765](https://github.com/appium/ruby_lib/commit/457e7657175e6c9ba981ca476098469e91e522e1) Update rubocop requirement from ~> 0.61.0 to ~> 0.68.1 (#875)
25
+ - [9a2b9fa](https://github.com/appium/ruby_lib/commit/9a2b9fa736877141f92d35b9e2ae97657d9955d7) Update rake requirement from ~> 12.0 to ~> 13.0 (#876)
26
+ - [8748244](https://github.com/appium/ruby_lib/commit/8748244f75ae9344e8b22dd20b2d42fb0bd84d75) fix ruby27 warning (#872)
27
+ - [f1734ab](https://github.com/appium/ruby_lib/commit/f1734ab0ff2a210463ab6a83120a673e1b6efd4a) ci: add 2.7 (#871)
28
+ - [b26a8f7](https://github.com/appium/ruby_lib/commit/b26a8f7c00c966ee3f193b4c97255a4751f0041e) fix a yardoc syntax
29
+
30
+
31
+ #### v10.5.0 2019-11-09
32
+
33
+ - [722c3a9](https://github.com/appium/ruby_lib/commit/722c3a9aa387d3537b648a50dc24070e62fcc57a) Release 10.5.0
34
+ - [7a2197b](https://github.com/appium/ruby_lib/commit/7a2197b46f6ec15f1adc8626bbff1be74640e98e) feat: Add log event/s (#868)
35
+
36
+
37
+ #### v10.4.1 2019-09-11
38
+
39
+ - [12317e4](https://github.com/appium/ruby_lib/commit/12317e4d74b20d01813526b28e642e3522193fa3) Release 10.4.1
40
+ - [58218e1](https://github.com/appium/ruby_lib/commit/58218e18f74df362e874b2a39d9e922b27b6bd5e) fix ios page (#866)
41
+ - [7f010e7](https://github.com/appium/ruby_lib/commit/7f010e70149847ae5af8dcf08eae889311f78e6d) fix typo in a comment
42
+
43
+
1
44
  #### v10.4.0 2019-07-19
2
45
 
3
46
  - [954a3a8](https://github.com/appium/ruby_lib/commit/954a3a8d4d5cd56fa5a4e80d021805759dbf5e10) Release 10.4.0