appium_lib 9.0.0 → 9.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.
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