appium_lib 9.0.0 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -0
  3. data/android_tests/lib/android/specs/android/helper.rb +3 -5
  4. data/android_tests/lib/android/specs/common/command.rb +47 -0
  5. data/android_tests/lib/android/specs/common/helper.rb +8 -8
  6. data/android_tests/lib/android/specs/common/web_context.rb +1 -1
  7. data/android_tests/lib/android/specs/driver.rb +19 -11
  8. data/android_tests/lib/format.rb +3 -3
  9. data/android_tests/lib/run.rb +7 -5
  10. data/android_tests/readme.md +1 -1
  11. data/appium_lib.gemspec +6 -7
  12. data/contributing.md +10 -0
  13. data/docs/android_docs.md +252 -217
  14. data/docs/ios_docs.md +257 -222
  15. data/docs/ios_xcuitest.md +32 -4
  16. data/ios_tests/lib/common.rb +11 -55
  17. data/ios_tests/lib/ios/specs/common/command.rb +44 -0
  18. data/ios_tests/lib/ios/specs/common/helper.rb +8 -8
  19. data/ios_tests/lib/ios/specs/device/device.rb +5 -5
  20. data/ios_tests/lib/ios/specs/driver.rb +34 -15
  21. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +1 -1
  22. data/ios_tests/readme.md +1 -1
  23. data/ios_tests/upload/sauce_storage.rb +10 -8
  24. data/lib/appium_lib/android/element/button.rb +3 -3
  25. data/lib/appium_lib/android/element/text.rb +1 -1
  26. data/lib/appium_lib/android/element/textfield.rb +1 -1
  27. data/lib/appium_lib/android/helper.rb +12 -12
  28. data/lib/appium_lib/android/mobile_methods.rb +1 -3
  29. data/lib/appium_lib/common/command.rb +55 -0
  30. data/lib/appium_lib/common/helper.rb +7 -7
  31. data/lib/appium_lib/common/patch.rb +21 -8
  32. data/lib/appium_lib/common/search_context.rb +10 -0
  33. data/lib/appium_lib/common/version.rb +2 -2
  34. data/lib/appium_lib/common/wait.rb +8 -14
  35. data/lib/appium_lib/device/device.rb +65 -83
  36. data/lib/appium_lib/device/multi_touch.rb +2 -2
  37. data/lib/appium_lib/device/touch_actions.rb +4 -7
  38. data/lib/appium_lib/driver.rb +66 -38
  39. data/lib/appium_lib/ios/element/alert.rb +4 -4
  40. data/lib/appium_lib/ios/element/textfield.rb +3 -3
  41. data/lib/appium_lib/ios/errors.rb +6 -0
  42. data/lib/appium_lib/ios/helper.rb +25 -25
  43. data/lib/appium_lib/ios/mobile_methods.rb +2 -4
  44. data/readme.md +3 -1
  45. data/release_notes.md +13 -0
  46. metadata +23 -12
@@ -1,7 +1,7 @@
1
1
  # TextView methods
2
2
  module Appium
3
3
  module Android
4
- TextView = 'android.widget.TextView'
4
+ TextView = 'android.widget.TextView'.freeze
5
5
 
6
6
  # Find the first TextView that contains value or by index.
7
7
  # @param value [String, Integer] the value to find.
@@ -1,6 +1,6 @@
1
1
  module Appium
2
2
  module Android
3
- EditText = 'android.widget.EditText'
3
+ EditText = 'android.widget.EditText'.freeze
4
4
 
5
5
  # Find the first EditText that contains value or by index.
6
6
  # @param value [String, Integer] the text to match exactly.
@@ -50,8 +50,8 @@ module Appium
50
50
 
51
51
  string_ids = nil
52
52
 
53
- if id_matches && id_matches.length > 0
54
- space_suffix = ' ' * ' strings.xml: '.length
53
+ if id_matches && !id_matches.empty?
54
+ space_suffix = ' ' * 15 # 15 is ' strings.xml: '.length
55
55
  string_ids = ''
56
56
 
57
57
  # add first
@@ -130,11 +130,11 @@ module Appium
130
130
  source_header = source[0..doctype_string.length].downcase
131
131
  source_is_html = source_header.start_with?(doctype_string, '<html')
132
132
 
133
- if source_is_html # parse html from webview
134
- parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
135
- else
136
- parser = @android_native_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
137
- end
133
+ parser = if source_is_html # parse html from webview
134
+ @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
135
+ else
136
+ @android_native_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
137
+ end
138
138
  parser.document.reset # ensure document is reset before parsing
139
139
  parser.document.filter = class_name
140
140
  parser.parse source
@@ -208,7 +208,7 @@ module Appium
208
208
  index = results.length
209
209
  index -= 1 if index >= 0
210
210
  else
211
- fail 'Index must be >= 1' unless index >= 1
211
+ raise 'Index must be >= 1' unless index >= 1
212
212
  index -= 1 if index >= 1
213
213
  end
214
214
 
@@ -289,9 +289,9 @@ module Appium
289
289
  value = %("#{value}")
290
290
 
291
291
  if class_name == '*'
292
- return _resource_id(value, "new UiSelector().resourceId(#{value});") +
292
+ return (_resource_id(value, "new UiSelector().resourceId(#{value});") +
293
293
  "new UiSelector().descriptionContains(#{value});" \
294
- "new UiSelector().textContains(#{value});"
294
+ "new UiSelector().textContains(#{value});")
295
295
  end
296
296
 
297
297
  class_name = %("#{class_name}")
@@ -326,9 +326,9 @@ module Appium
326
326
  value = %("#{value}")
327
327
 
328
328
  if class_name == '*'
329
- return _resource_id(value, "new UiSelector().resourceId(#{value});") +
329
+ return (_resource_id(value, "new UiSelector().resourceId(#{value});") +
330
330
  "new UiSelector().description(#{value});" \
331
- "new UiSelector().text(#{value});"
331
+ "new UiSelector().text(#{value});")
332
332
  end
333
333
 
334
334
  class_name = %("#{class_name}")
@@ -8,9 +8,7 @@ module Appium
8
8
  # find_elements :uiautomator, 'new UiSelector().clickable(true)'
9
9
  # ```
10
10
  def extended(_mod)
11
- Selenium::WebDriver::SearchContext.class_eval do
12
- Selenium::WebDriver::SearchContext::FINDERS[:uiautomator] = '-android uiautomator'
13
- end
11
+ ::Appium::Driver::SearchContext::FINDERS[:uiautomator] = '-android uiautomator'
14
12
  end
15
13
  end # class << self
16
14
  end # module Android
@@ -0,0 +1,55 @@
1
+ module Appium
2
+ class Driver
3
+ module Commands
4
+ COMMAND_NO_ARG = {
5
+ # common
6
+ shake: [:post, 'session/:session_id/appium/device/shake'.freeze],
7
+ launch_app: [:post, 'session/:session_id/appium/app/launch'.freeze],
8
+ close_app: [:post, 'session/:session_id/appium/app/close'.freeze],
9
+ reset: [:post, 'session/:session_id/appium/app/reset'.freeze],
10
+ device_locked?: [:post, 'session/:session_id/appium/device/is_locked'.freeze],
11
+
12
+ # Android
13
+ open_notifications: [:post, 'session/:session_id/appium/device/open_notifications'.freeze],
14
+ toggle_airplane_mode: [:post, 'session/:session_id/appium/device/toggle_airplane_mode'.freeze],
15
+ current_activity: [:get, 'session/:session_id/appium/device/current_activity'.freeze],
16
+ get_network_connection: [:get, 'session/:session_id/network_connection'.freeze],
17
+
18
+ # iOS
19
+ device_time: [:get, 'session/:session_id/appium/device/system_time'.freeze],
20
+ current_context: [:get, 'session/:session_id/context'.freeze]
21
+ }.freeze
22
+
23
+ COMMAND = {
24
+ # common
25
+ available_contexts: [:get, 'session/:session_id/contexts'.freeze],
26
+ set_context: [:post, 'session/:session_id/context'.freeze],
27
+ app_strings: [:post, 'session/:session_id/appium/app/strings'.freeze],
28
+ lock: [:post, 'session/:session_id/appium/device/lock'.freeze],
29
+ install_app: [:post, 'session/:session_id/appium/device/install_app'.freeze],
30
+ remove_app: [:post, 'session/:session_id/appium/device/remove_app'.freeze],
31
+ app_installed?: [:post, 'session/:session_id/appium/device/app_installed'.freeze],
32
+ background_app: [:post, 'session/:session_id/appium/app/background'.freeze],
33
+ hide_keyboard: [:post, 'session/:session_id/appium/device/hide_keyboard'.freeze],
34
+ press_keycode: [:post, 'session/:session_id/appium/device/press_keycode'.freeze],
35
+ long_press_keycode: [:post, 'session/:session_id/appium/device/long_press_keycode'.freeze],
36
+ set_immediate_value: [:post, 'session/:session_id/appium/element/:id/value'.freeze],
37
+ push_file: [:post, 'session/:session_id/appium/device/push_file'.freeze],
38
+ pull_file: [:post, 'session/:session_id/appium/device/pull_file'.freeze],
39
+ pull_folder: [:post, 'session/:session_id/appium/device/pull_folder'.freeze],
40
+ get_settings: [:get, 'session/:session_id/appium/settings'.freeze],
41
+ update_settings: [:post, 'session/:session_id/appium/settings'.freeze],
42
+ touch_actions: [:post, 'session/:session_id/touch/perform'.freeze],
43
+ multi_touch: [:post, 'session/:session_id/touch/multi/perform'.freeze],
44
+
45
+ # Android
46
+ start_activity: [:post, 'session/:session_id/appium/device/start_activity'.freeze],
47
+ end_coverage: [:post, 'session/:session_id/appium/app/end_test_coverage'.freeze],
48
+ set_network_connection: [:post, 'session/:session_id/network_connection'.freeze],
49
+
50
+ # iOS
51
+ touch_id: [:post, 'session/:session_id/appium/simulator/touch_id'.freeze]
52
+ }.merge(COMMAND_NO_ARG).merge(::Selenium::WebDriver::Remote::Bridge::COMMANDS).freeze
53
+ end
54
+ end
55
+ end
@@ -55,11 +55,11 @@ module Appium
55
55
 
56
56
  def _print_source(source)
57
57
  opts = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
58
- if source.start_with? '<html'
59
- doc = Nokogiri::HTML(source) { |cfg| cfg.options = opts }
60
- else
61
- doc = Nokogiri::XML(source) { |cfg| cfg.options = opts }
62
- end
58
+ doc = if source.start_with? '<html'
59
+ Nokogiri::HTML(source) { |cfg| cfg.options = opts }
60
+ else
61
+ Nokogiri::XML(source) { |cfg| cfg.options = opts }
62
+ end
63
63
  puts doc.to_xml indent: 2
64
64
  end
65
65
 
@@ -215,8 +215,8 @@ module Appium
215
215
  end
216
216
 
217
217
  def _no_such_element
218
- fail Selenium::WebDriver::Error::NoSuchElementError,
219
- 'An element could not be located on the page using the given search parameters.'
218
+ error_message = 'An element could not be located on the page using the given search parameters.'
219
+ raise Selenium::WebDriver::Error::NoSuchElementError, error_message
220
220
  end
221
221
  end # module Common
222
222
  end # module Appium
@@ -52,10 +52,13 @@ end # module Appium
52
52
  #
53
53
  # Requires from lib/selenium/webdriver/remote.rb
54
54
  require 'selenium/webdriver/remote/capabilities'
55
+ require 'selenium/webdriver/remote/w3c_capabilities'
55
56
  require 'selenium/webdriver/remote/bridge'
57
+ require 'selenium/webdriver/remote/w3c_bridge'
56
58
  require 'selenium/webdriver/remote/server_error'
57
59
  require 'selenium/webdriver/remote/response'
58
60
  require 'selenium/webdriver/remote/commands'
61
+ require 'selenium/webdriver/remote/w3c_commands'
59
62
  require 'selenium/webdriver/remote/http/common'
60
63
  require 'selenium/webdriver/remote/http/default'
61
64
 
@@ -67,14 +70,15 @@ def patch_webdriver_bridge
67
70
  Selenium::WebDriver::Remote::Bridge.class_eval do
68
71
  # Code from lib/selenium/webdriver/remote/bridge.rb
69
72
  def raw_execute(command, opts = {}, command_hash = nil)
70
- verb, path = Selenium::WebDriver::Remote::Bridge::COMMANDS[command] ||
71
- fail(ArgumentError, "unknown command: #{command.inspect}")
72
- path = path.dup
73
+ verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
74
+ path = path.dup
73
75
 
74
76
  path[':session_id'] = @session_id if path.include?(':session_id')
75
77
 
76
78
  begin
77
- opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
79
+ opts.each do |key, value|
80
+ path[key.inspect] = escaper.escape(value.to_s)
81
+ end
78
82
  rescue IndexError
79
83
  raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
80
84
  end
@@ -107,10 +111,11 @@ def patch_webdriver_bridge
107
111
  else
108
112
  Appium::Logger.ap_info print_command
109
113
  end
110
- else # non-standard command hash
114
+ elsif command_hash
115
+ # non-standard command hash
111
116
  # It's important to output this for debugging problems.
112
117
  # for example invalid JSON will not be a Hash
113
- Appium::Logger.ap_info command_hash if command_hash
118
+ Appium::Logger.ap_info command_hash
114
119
  end
115
120
  delay = $driver.global_webdriver_http_sleep
116
121
  sleep delay if !delay.nil? && delay > 0
@@ -131,7 +136,7 @@ class Selenium::WebDriver::Remote::Response
131
136
  case val
132
137
  when Hash
133
138
  msg = val['origValue'] || val['message'] or return 'unknown error'
134
- msg << " (#{ val['class'] })" if val['class']
139
+ msg << " (#{val['class']})" if val['class']
135
140
  when String
136
141
  msg = val
137
142
  else
@@ -144,5 +149,13 @@ end
144
149
 
145
150
  class Selenium::WebDriver::Remote::Http::Common # rubocop:disable Style/ClassAndModuleChildren
146
151
  remove_const :DEFAULT_HEADERS if defined? DEFAULT_HEADERS
147
- DEFAULT_HEADERS = { 'Accept' => CONTENT_TYPE, 'User-Agent' => "appium/ruby_lib/#{::Appium::VERSION}" }
152
+ DEFAULT_HEADERS = { 'Accept' => CONTENT_TYPE, 'User-Agent' => "appium/ruby_lib/#{::Appium::VERSION}" }.freeze
153
+ end
154
+
155
+ def patch_remote_driver_commands
156
+ Selenium::WebDriver::Remote::Bridge.class_eval do
157
+ def commands(command)
158
+ ::Appium::Driver::Commands::COMMAND[command]
159
+ end
160
+ end
148
161
  end
@@ -0,0 +1,10 @@
1
+ module Appium
2
+ class Driver
3
+ module SearchContext
4
+ # rubocop:disable Style/MutableConstant
5
+ FINDERS = {
6
+ accessibility_id: 'accessibility id'
7
+ }
8
+ end
9
+ end
10
+ end
@@ -1,5 +1,5 @@
1
1
  module Appium
2
2
  # Version and Date are defined on the 'Appium' module, not 'Appium::Common'
3
- VERSION = '9.0.0' unless defined? ::Appium::VERSION
4
- DATE = '2016-12-09' unless defined? ::Appium::DATE
3
+ VERSION = '9.1.0'.freeze unless defined? ::Appium::VERSION
4
+ DATE = '2016-12-18'.freeze unless defined? ::Appium::DATE
5
5
  end
@@ -11,7 +11,7 @@ module Appium
11
11
  invalid_keys = []
12
12
  opts.keys.each { |key| invalid_keys << key unless valid_keys.include?(key) }
13
13
  # [:one, :two] => :one, :two
14
- fail "Invalid keys #{invalid_keys.to_s[1..-2]}. Valid keys are #{valid_keys.to_s[1..-2]}" unless invalid_keys.empty?
14
+ raise "Invalid keys #{invalid_keys.to_s[1..-2]}. Valid keys are #{valid_keys.to_s[1..-2]}" unless invalid_keys.empty?
15
15
 
16
16
  timeout = opts.fetch(:timeout, 30)
17
17
  interval = opts.fetch(:interval, 0.5)
@@ -24,12 +24,10 @@ module Appium
24
24
 
25
25
  until Time.now > end_time
26
26
  begin
27
- if return_if_true
28
- result = block.call
29
- return result if result
30
- else
31
- return block.call
32
- end
27
+ return block.call unless return_if_true
28
+
29
+ result = block.call
30
+ return result if result
33
31
  rescue ::Errno::ECONNREFUSED => e
34
32
  raise e
35
33
  rescue *ignored => last_error # rubocop:disable Lint/HandleExceptions
@@ -39,21 +37,17 @@ module Appium
39
37
  sleep interval
40
38
  end
41
39
 
42
- if message
43
- msg = message.dup
44
- else
45
- msg = "timed out after #{timeout} seconds"
46
- end
40
+ msg = message ? message.dup : "timed out after #{timeout} seconds"
47
41
 
48
42
  msg << " (#{last_error.message})" if last_error
49
43
 
50
- fail Selenium::WebDriver::Error::TimeOutError, msg
44
+ raise Selenium::WebDriver::Error::TimeOutError, msg
51
45
  end
52
46
 
53
47
  # process opts before calling _generic_wait
54
48
  def _process_wait_opts(opts)
55
49
  opts = { timeout: opts } if opts.is_a?(Numeric)
56
- fail 'opts must be a hash' unless opts.is_a? Hash
50
+ raise 'opts must be a hash' unless opts.is_a? Hash
57
51
  opts
58
52
  end
59
53
 
@@ -4,24 +4,6 @@ module Appium
4
4
  module Device
5
5
  extend Forwardable
6
6
 
7
- NoArgMethods = {
8
- post: {
9
- open_notifications: 'session/:session_id/appium/device/open_notifications',
10
- shake: 'session/:session_id/appium/device/shake',
11
- launch_app: 'session/:session_id/appium/app/launch',
12
- close_app: 'session/:session_id/appium/app/close',
13
- reset: 'session/:session_id/appium/app/reset',
14
- toggle_airplane_mode: 'session/:session_id/appium/device/toggle_airplane_mode',
15
- device_locked?: 'session/:session_id/appium/device/is_locked'
16
- },
17
- get: {
18
- device_time: 'session/:session_id/appium/device/system_time',
19
- current_activity: 'session/:session_id/appium/device/current_activity',
20
- current_context: 'session/:session_id/context',
21
- get_network_connection: 'session/:session_id/network_connection'
22
- }
23
- }
24
-
25
7
  # @!method app_strings
26
8
  # Return the hash of all localization strings.
27
9
  # ```ruby
@@ -117,49 +99,49 @@ module Appium
117
99
  def extended(_mod)
118
100
  extend_webdriver_with_forwardable
119
101
 
120
- NoArgMethods.each_pair do |verb, pair|
121
- pair.each_pair { |command, path| add_endpoint_method command, path, verb }
102
+ ::Appium::Driver::Commands::COMMAND_NO_ARG.each_key do |method|
103
+ add_endpoint_method method
122
104
  end
123
105
 
124
- add_endpoint_method(:available_contexts, 'session/:session_id/contexts', :get) do
106
+ add_endpoint_method(:available_contexts) do
125
107
  def available_contexts
126
108
  # return empty array instead of nil on failure
127
109
  execute(:available_contexts, {}) || []
128
110
  end
129
111
  end
130
112
 
131
- add_endpoint_method(:app_strings, 'session/:session_id/appium/app/strings') do
113
+ add_endpoint_method(:app_strings) do
132
114
  def app_strings(language = nil)
133
115
  opts = language ? { language: language } : {}
134
116
  execute :app_strings, {}, opts
135
117
  end
136
118
  end
137
119
 
138
- add_endpoint_method(:lock, 'session/:session_id/appium/device/lock') do
120
+ add_endpoint_method(:lock) do
139
121
  def lock(duration)
140
122
  execute :lock, {}, seconds: duration
141
123
  end
142
124
  end
143
125
 
144
- add_endpoint_method(:install_app, 'session/:session_id/appium/device/install_app') do
126
+ add_endpoint_method(:install_app) do
145
127
  def install_app(path)
146
128
  execute :install_app, {}, appPath: path
147
129
  end
148
130
  end
149
131
 
150
- add_endpoint_method(:remove_app, 'session/:session_id/appium/device/remove_app') do
132
+ add_endpoint_method(:remove_app) do
151
133
  def remove_app(id)
152
134
  execute :remove_app, {}, appId: id
153
135
  end
154
136
  end
155
137
 
156
- add_endpoint_method(:app_installed?, 'session/:session_id/appium/device/app_installed') do
138
+ add_endpoint_method(:app_installed?) do
157
139
  def app_installed?(app_id)
158
140
  execute :app_installed?, {}, bundleId: app_id
159
141
  end
160
142
  end
161
143
 
162
- add_endpoint_method(:background_app, 'session/:session_id/appium/app/background') do
144
+ add_endpoint_method(:background_app) do
163
145
  def background_app(duration)
164
146
  execute :background_app, {}, seconds: duration
165
147
  end
@@ -178,18 +160,18 @@ module Appium
178
160
  # start_activity app_package: 'io.appium.android.apis',
179
161
  # app_activity: '.accessibility.AccessibilityNodeProviderActivity'
180
162
  # ```
181
- add_endpoint_method(:start_activity, 'session/:session_id/appium/device/start_activity') do
163
+ add_endpoint_method(:start_activity) do
182
164
  def start_activity(opts)
183
- fail 'opts must be a hash' unless opts.is_a? Hash
165
+ raise 'opts must be a hash' unless opts.is_a? Hash
184
166
  app_package = opts[:app_package]
185
- fail 'app_package is required' unless app_package
167
+ raise 'app_package is required' unless app_package
186
168
  app_activity = opts[:app_activity]
187
- fail 'app_activity is required' unless opts[:app_activity]
169
+ raise 'app_activity is required' unless opts[:app_activity]
188
170
  app_wait_package = opts.fetch(:app_wait_package, '')
189
171
  app_wait_activity = opts.fetch(:app_wait_activity, '')
190
172
 
191
173
  unknown_opts = opts.keys - [:app_package, :app_activity, :app_wait_package, :app_wait_activity]
192
- fail "Unknown options #{unknown_opts}" unless unknown_opts.empty?
174
+ raise "Unknown options #{unknown_opts}" unless unknown_opts.empty?
193
175
 
194
176
  execute :start_activity, {}, appPackage: app_package,
195
177
  appActivity: app_activity,
@@ -198,13 +180,13 @@ module Appium
198
180
  end
199
181
  end
200
182
 
201
- add_endpoint_method(:set_context, 'session/:session_id/context') do
183
+ add_endpoint_method(:set_context) do
202
184
  def set_context(context = null)
203
185
  execute :set_context, {}, name: context
204
186
  end
205
187
  end
206
188
 
207
- add_endpoint_method(:hide_keyboard, 'session/:session_id/appium/device/hide_keyboard') do
189
+ add_endpoint_method(:hide_keyboard) do
208
190
  def hide_keyboard(close_key = nil)
209
191
  # Android can only tapOutside.
210
192
  if $driver.device_is_android?
@@ -222,7 +204,7 @@ module Appium
222
204
  end
223
205
  end
224
206
 
225
- add_endpoint_method(:press_keycode, 'session/:session_id/appium/device/press_keycode') do
207
+ add_endpoint_method(:press_keycode) do
226
208
  def press_keycode(key, metastate = nil)
227
209
  args = { keycode: key }
228
210
  args[:metastate] = metastate if metastate
@@ -230,7 +212,7 @@ module Appium
230
212
  end
231
213
  end
232
214
 
233
- add_endpoint_method(:long_press_keycode, 'session/:session_id/appium/device/long_press_keycode') do
215
+ add_endpoint_method(:long_press_keycode) do
234
216
  def long_press_keycode(key, metastate = nil)
235
217
  args = { keycode: key }
236
218
  args[:metastate] = metastate if metastate
@@ -239,20 +221,20 @@ module Appium
239
221
  end
240
222
 
241
223
  # TODO: TEST ME
242
- add_endpoint_method(:set_immediate_value, 'session/:session_id/appium/element/:id/value') do
224
+ add_endpoint_method(:set_immediate_value) do
243
225
  def set_immediate_value(element, value)
244
226
  execute :set_immediate_value, { id: element.ref }, value: value
245
227
  end
246
228
  end
247
229
 
248
- add_endpoint_method(:push_file, 'session/:session_id/appium/device/push_file') do
230
+ add_endpoint_method(:push_file) do
249
231
  def push_file(path, filedata)
250
232
  encoded_data = Base64.encode64 filedata
251
233
  execute :push_file, {}, path: path, data: encoded_data
252
234
  end
253
235
  end
254
236
 
255
- add_endpoint_method(:pull_file, 'session/:session_id/appium/device/pull_file') do
237
+ add_endpoint_method(:pull_file) do
256
238
  def pull_file(path)
257
239
  data = execute :pull_file, {}, path: path
258
240
  Base64.decode64 data
@@ -260,7 +242,7 @@ module Appium
260
242
  end
261
243
 
262
244
  # TODO: TEST ME
263
- add_endpoint_method(:pull_folder, 'session/:session_id/appium/device/pull_folder') do
245
+ add_endpoint_method(:pull_folder) do
264
246
  def pull_folder(path)
265
247
  data = execute :pull_folder, {}, path: path
266
248
  Base64.decode64 data
@@ -268,26 +250,26 @@ module Appium
268
250
  end
269
251
 
270
252
  # TODO: TEST ME
271
- add_endpoint_method(:touch_id, 'session/:session_id/appium/simulator/touch_id') do
253
+ add_endpoint_method(:touch_id) do
272
254
  def touch_id(match = true)
273
255
  execute :touch_id, {}, match: match
274
256
  end
275
257
  end
276
258
 
277
259
  # TODO: TEST ME
278
- add_endpoint_method(:end_coverage, 'session/:session_id/appium/app/end_test_coverage') do
260
+ add_endpoint_method(:end_coverage) do
279
261
  def end_coverage(path, intent)
280
262
  execute :end_coverage, {}, path: path, intent: intent
281
263
  end
282
264
  end
283
265
 
284
- add_endpoint_method(:get_settings, 'session/:session_id/appium/settings', :get) do
266
+ add_endpoint_method(:get_settings) do
285
267
  def get_settings
286
268
  execute :get_settings, {}
287
269
  end
288
270
  end
289
271
 
290
- add_endpoint_method(:update_settings, 'session/:session_id/appium/settings') do
272
+ add_endpoint_method(:update_settings) do
291
273
  def update_settings(settings)
292
274
  execute :update_settings, {}, settings: settings
293
275
  end
@@ -308,7 +290,7 @@ module Appium
308
290
  # 2 (Wifi only) | 0 | 1 | 0
309
291
  # 0 (None) | 0 | 0 | 0
310
292
  #
311
- add_endpoint_method(:set_network_connection, 'session/:session_id/network_connection') do
293
+ add_endpoint_method(:set_network_connection) do
312
294
  def set_network_connection(mode)
313
295
  execute :set_network_connection, {}, type: mode
314
296
  end
@@ -322,13 +304,11 @@ module Appium
322
304
  # def extended
323
305
 
324
306
  # @private
325
- def add_endpoint_method(method, path, verb = :post)
307
+ def add_endpoint_method(method)
326
308
  if block_given?
327
- # &Proc.new with no args passes the passed_in block
328
- # Because creating Procs from blocks is slow
329
- create_bridge_command method, verb, path, &Proc.new
309
+ create_bridge_command method, &Proc.new
330
310
  else
331
- create_bridge_command method, verb, path
311
+ create_bridge_command method
332
312
  end
333
313
 
334
314
  delegate_driver_method method
@@ -355,31 +335,7 @@ module Appium
355
335
  end
356
336
 
357
337
  # @private
358
- def create_bridge_command(method, verb, path)
359
- Selenium::WebDriver::Remote::Bridge.class_eval do
360
- command method, verb, path
361
- if block_given?
362
- class_eval(&Proc.new)
363
- else
364
- define_method(method) { execute method }
365
- end
366
- end
367
- end
368
-
369
- # @private
370
- def add_bridge_method(method)
371
- if block_given?
372
- create_bridge method, &Proc.new
373
- else
374
- create_bridge method
375
- end
376
-
377
- delegate_driver_method method
378
- delegate_from_appium_driver method
379
- end
380
-
381
- # @private
382
- def create_bridge(method)
338
+ def create_bridge_command(method)
383
339
  Selenium::WebDriver::Remote::Bridge.class_eval do
384
340
  if block_given?
385
341
  class_eval(&Proc.new)
@@ -399,19 +355,45 @@ module Appium
399
355
  # ```
400
356
  def extend_search_contexts
401
357
  Selenium::WebDriver::SearchContext.class_eval do
402
- Selenium::WebDriver::SearchContext::FINDERS[:accessibility_id] = 'accessibility id'
358
+ def find_element_with_appium(*args)
359
+ how, what = extract_args(args)
360
+
361
+ finders = ::Selenium::WebDriver::SearchContext::FINDERS.merge ::Appium::Driver::SearchContext::FINDERS
362
+ by = finders[how.to_sym]
363
+ raise ArgumentError, "cannot find element by #{how.inspect}" unless by
364
+
365
+ begin
366
+ bridge.find_element_by by, what.to_s, ref
367
+ rescue Selenium::WebDriver::Error::TimeOutError
368
+ raise Selenium::WebDriver::Error::NoSuchElementError
369
+ end
370
+ end
371
+
372
+ def find_elements_with_appium(*args)
373
+ how, what = extract_args(args)
374
+
375
+ finders = ::Selenium::WebDriver::SearchContext::FINDERS.merge ::Appium::Driver::SearchContext::FINDERS
376
+ by = finders[how.to_sym]
377
+ raise ArgumentError, "cannot find element by #{how.inspect}" unless by
378
+
379
+ begin
380
+ bridge.find_elements_by by, what.to_s, ref
381
+ rescue Selenium::WebDriver::Error::TimeOutError
382
+ raise Selenium::WebDriver::Error::NoSuchElementError
383
+ end
384
+ end
403
385
  end
404
386
  end
405
387
 
406
388
  def add_touch_actions
407
- add_endpoint_method(:touch_actions, 'session/:session_id/touch/perform') do
389
+ add_endpoint_method(:touch_actions) do
408
390
  def touch_actions(actions)
409
391
  actions = { actions: [actions].flatten }
410
392
  execute :touch_actions, {}, actions
411
393
  end
412
394
  end
413
395
 
414
- add_endpoint_method(:multi_touch, 'session/:session_id/touch/multi/perform') do
396
+ add_endpoint_method(:multi_touch) do
415
397
  def multi_touch(actions)
416
398
  execute :multi_touch, {}, actions: actions
417
399
  end
@@ -440,7 +422,7 @@ module Appium
440
422
  # ```ruby
441
423
  # ime_activate engine: 'com.android.inputmethod.latin/.LatinIME'
442
424
  # ```
443
- add_bridge_method(:ime_activate) do
425
+ add_endpoint_method(:ime_activate) do
444
426
  def ime_activate(ime_name)
445
427
  execute :imeActivateEngine, {}, engine: ime_name
446
428
  end
@@ -453,7 +435,7 @@ module Appium
453
435
  # ```ruby
454
436
  # ime_available_engines #=> Get the list of IME installed in the target device
455
437
  # ```
456
- add_bridge_method(:ime_available_engines) do
438
+ add_endpoint_method(:ime_available_engines) do
457
439
  def ime_available_engines
458
440
  execute :imeGetAvailableEngines
459
441
  end
@@ -466,7 +448,7 @@ module Appium
466
448
  # ```ruby
467
449
  # ime_active_engine #=> Get the current active IME such as 'com.android.inputmethod.latin/.LatinIME'
468
450
  # ```
469
- add_bridge_method(:ime_active_engine) do
451
+ add_endpoint_method(:ime_active_engine) do
470
452
  def ime_active_engine
471
453
  execute :imeGetActiveEngine
472
454
  end
@@ -479,7 +461,7 @@ module Appium
479
461
  # ```ruby
480
462
  # ime_activated #=> True if IME is activated
481
463
  # ```
482
- add_bridge_method(:ime_activated) do
464
+ add_endpoint_method(:ime_activated) do
483
465
  def ime_activated
484
466
  execute :imeIsActivated
485
467
  end
@@ -493,7 +475,7 @@ module Appium
493
475
  # ```ruby
494
476
  # ime_deactivate #=> Deactivate current IME engine
495
477
  # ```
496
- add_bridge_method(:ime_deactivate) do
478
+ add_endpoint_method(:ime_deactivate) do
497
479
  def ime_deactivate
498
480
  execute :imeDeactivate, {}
499
481
  end