appium_lib_core 0.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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rubocop.yml +20 -0
  4. data/.travis.yml +16 -0
  5. data/CHANGELOG.md +11 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +202 -0
  8. data/README.md +5 -0
  9. data/Rakefile +62 -0
  10. data/Thorfile +7 -0
  11. data/appium_lib_core.gemspec +35 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/lib/appium_lib_core.rb +41 -0
  15. data/lib/appium_lib_core/android.rb +5 -0
  16. data/lib/appium_lib_core/android/device.rb +174 -0
  17. data/lib/appium_lib_core/android/espresso/bridge.rb +16 -0
  18. data/lib/appium_lib_core/android/search_context.rb +18 -0
  19. data/lib/appium_lib_core/android/touch.rb +17 -0
  20. data/lib/appium_lib_core/android/uiautomator1/bridge.rb +16 -0
  21. data/lib/appium_lib_core/android/uiautomator2/bridge.rb +16 -0
  22. data/lib/appium_lib_core/android_espresso.rb +5 -0
  23. data/lib/appium_lib_core/android_uiautomator2.rb +5 -0
  24. data/lib/appium_lib_core/common.rb +6 -0
  25. data/lib/appium_lib_core/common/base.rb +8 -0
  26. data/lib/appium_lib_core/common/base/bridge.rb +47 -0
  27. data/lib/appium_lib_core/common/base/capabilities.rb +16 -0
  28. data/lib/appium_lib_core/common/base/command.rb +10 -0
  29. data/lib/appium_lib_core/common/base/driver.rb +51 -0
  30. data/lib/appium_lib_core/common/base/http_default.rb +13 -0
  31. data/lib/appium_lib_core/common/base/search_context.rb +89 -0
  32. data/lib/appium_lib_core/common/base/wait.rb +56 -0
  33. data/lib/appium_lib_core/common/command.rb +77 -0
  34. data/lib/appium_lib_core/common/device.rb +567 -0
  35. data/lib/appium_lib_core/common/error.rb +18 -0
  36. data/lib/appium_lib_core/common/log.rb +30 -0
  37. data/lib/appium_lib_core/common/logger.rb +30 -0
  38. data/lib/appium_lib_core/device/multi_touch.rb +48 -0
  39. data/lib/appium_lib_core/device/touch_actions.rb +191 -0
  40. data/lib/appium_lib_core/driver.rb +459 -0
  41. data/lib/appium_lib_core/ios.rb +7 -0
  42. data/lib/appium_lib_core/ios/device.rb +54 -0
  43. data/lib/appium_lib_core/ios/search_context.rb +27 -0
  44. data/lib/appium_lib_core/ios/touch.rb +17 -0
  45. data/lib/appium_lib_core/ios/uiautomation/bridge.rb +18 -0
  46. data/lib/appium_lib_core/ios/uiautomation/patch.rb +20 -0
  47. data/lib/appium_lib_core/ios/xcuitest/bridge.rb +18 -0
  48. data/lib/appium_lib_core/ios/xcuitest/device.rb +66 -0
  49. data/lib/appium_lib_core/ios/xcuitest/search_context.rb +40 -0
  50. data/lib/appium_lib_core/ios_xcuitest.rb +8 -0
  51. data/lib/appium_lib_core/patch.rb +78 -0
  52. data/lib/appium_lib_core/version.rb +6 -0
  53. data/release_notes.md +0 -0
  54. metadata +262 -0
@@ -0,0 +1,89 @@
1
+ module Appium
2
+ module Core
3
+ class Base
4
+ module SearchContext
5
+ # referenced: ::Selenium::WebDriver::SearchContext
6
+
7
+ FINDERS = ::Selenium::WebDriver::SearchContext::FINDERS.merge(accessibility_id: 'accessibility id')
8
+
9
+ def self.add_finders(finders)
10
+ FINDERS.merge!(finders)
11
+ end
12
+
13
+ #
14
+ # Find the first element matching the given arguments
15
+ #
16
+ # @overload find_element(how, what)
17
+ # @param [Symbol, String] how The method to find the element by
18
+ # @param [String] what The locator to use
19
+ # @overload find_element(opts)
20
+ # @param [Hash] opts Find options
21
+ # @option opts [Symbol] :how Key named after the method to find the element by, containing the locator
22
+ # @return [Element]
23
+ #
24
+ # @raise [Error::NoSuchElementError] if the element doesn't exist
25
+ #
26
+ # @example Find element with accessibility id
27
+ # find_elements :accessibility_id, 'Animation'
28
+ # find_elements :accessibility_id, 'Animation'
29
+ #
30
+ def find_element(*args)
31
+ how, what = extract_args(args)
32
+ by = _set_by_from_finders(how)
33
+ begin
34
+ bridge.find_element_by by, what.to_s, ref
35
+ rescue Selenium::WebDriver::Error::TimeOutError
36
+ raise Selenium::WebDriver::Error::NoSuchElementError
37
+ end
38
+ end
39
+
40
+ #
41
+ # Find all elements matching the given arguments
42
+ #
43
+ # @see SearchContext#find_element
44
+ #
45
+ def find_elements(*args)
46
+ how, what = extract_args(args)
47
+ by = _set_by_from_finders(how)
48
+ begin
49
+ bridge.find_elements_by by, what.to_s, ref
50
+ rescue Selenium::WebDriver::Error::TimeOutError
51
+ raise Selenium::WebDriver::Error::NoSuchElementError
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def _set_by_from_finders(how)
58
+ finders = FINDERS
59
+ by = finders[how.to_sym]
60
+ raise ArgumentError, "cannot find element by #{how.inspect}" unless by
61
+ by
62
+ end
63
+
64
+ def extract_args(args)
65
+ case args.size
66
+ when 2
67
+ args
68
+ when 1
69
+ arg = args.first
70
+
71
+ unless arg.respond_to?(:shift)
72
+ raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
73
+ end
74
+
75
+ # this will be a single-entry hash, so use #shift over #first or #[]
76
+ arr = arg.dup.shift
77
+ unless arr.size == 2
78
+ raise ArgumentError, "expected #{arr.inspect} to have 2 elements"
79
+ end
80
+
81
+ arr
82
+ else
83
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
84
+ end
85
+ end
86
+ end # module SearchContext
87
+ end # class Base
88
+ end # module Core
89
+ end # module Appium
@@ -0,0 +1,56 @@
1
+ # rubocop:disable Lint/HandleExceptions
2
+ module Appium
3
+ module Core
4
+ class Base
5
+ class Wait < ::Selenium::WebDriver::Wait
6
+ require 'timeout' # for wait
7
+
8
+ def initialize(opts = {})
9
+ valid_keys = [:timeout, :interval, :message, :ignore, :return_if_true]
10
+ invalid_keys = []
11
+ opts.keys.each { |key| invalid_keys << key unless valid_keys.include?(key) }
12
+ # [:one, :two] => :one, :two
13
+ unless invalid_keys.empty?
14
+ raise "Invalid keys #{invalid_keys.to_s[1..-2]}. Valid keys are #{valid_keys.to_s[1..-2]}"
15
+ end
16
+
17
+ @timeout = opts.fetch(:timeout, DEFAULT_TIMEOUT)
18
+ @interval = opts.fetch(:interval, DEFAULT_INTERVAL)
19
+ @message = opts[:message]
20
+ @ignored = Array(opts[:ignore] || ::Exception)
21
+ @return_if_true = opts[:return_if_true]
22
+
23
+ super(timeout: @timeout, interval: @interval, message: @message, ignore: @ignored)
24
+ end
25
+
26
+ # Wait code from the selenium Ruby gem
27
+ # https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
28
+ def until
29
+ end_time = Time.now + @timeout
30
+ last_error = nil
31
+
32
+ until Time.now > end_time
33
+ begin
34
+ return yield unless @return_if_true
35
+
36
+ result = yield
37
+ return result if result
38
+ rescue ::Errno::ECONNREFUSED => e
39
+ raise e
40
+ rescue *@ignored => last_error
41
+ # swallowed
42
+ end
43
+
44
+ sleep @interval
45
+ end
46
+
47
+ msg = @message ? @message.dup : "timed out after #{@timeout} seconds"
48
+
49
+ msg << " (#{last_error.message})" if last_error
50
+
51
+ raise Selenium::WebDriver::Error::TimeOutError, msg
52
+ end
53
+ end # module Wait
54
+ end # module Base
55
+ end # module Core
56
+ end # module Appium
@@ -0,0 +1,77 @@
1
+ require_relative 'base/command'
2
+
3
+ module Appium
4
+ module Core
5
+ # ref: https://github.com/appium/appium-base-driver/blob/master/lib/mjsonwp/routes.js
6
+ module Commands
7
+ COMMAND_NO_ARG = {
8
+ # Common
9
+ shake: [:post, 'session/:session_id/appium/device/shake'.freeze],
10
+ launch_app: [:post, 'session/:session_id/appium/app/launch'.freeze],
11
+ close_app: [:post, 'session/:session_id/appium/app/close'.freeze],
12
+ reset: [:post, 'session/:session_id/appium/app/reset'.freeze],
13
+ device_locked?: [:post, 'session/:session_id/appium/device/is_locked'.freeze],
14
+ unlock: [:post, 'session/:session_id/appium/device/unlock'.freeze],
15
+ device_time: [:get, 'session/:session_id/appium/device/system_time'.freeze],
16
+ current_context: [:get, 'session/:session_id/context'.freeze],
17
+
18
+ # Android
19
+ open_notifications: [:post, 'session/:session_id/appium/device/open_notifications'.freeze],
20
+ toggle_airplane_mode: [:post, 'session/:session_id/appium/device/toggle_airplane_mode'.freeze],
21
+ current_activity: [:get, 'session/:session_id/appium/device/current_activity'.freeze],
22
+ current_package: [:get, 'session/:session_id/appium/device/current_package'.freeze],
23
+ get_system_bars: [:get, 'session/:session_id/appium/device/system_bars'.freeze],
24
+ get_display_density: [:get, 'session/:session_id/appium/device/display_density'.freeze],
25
+ is_keyboard_shown: [:get, 'session/:session_id/appium/device/is_keyboard_shown'.freeze],
26
+ get_network_connection: [:get, 'session/:session_id/network_connection'.freeze],
27
+ get_performance_data_types: [:post, 'session/:session_id/appium/performanceData/types'.freeze],
28
+ stop_recording_screen: [:post, 'session/:session_id/appium/stop_recording_screen'.freeze]
29
+ # iOS
30
+ }.freeze
31
+
32
+ # Some commands differ for each driver.
33
+ COMMAND = {
34
+ # common
35
+ available_contexts: [:get, 'session/:session_id/contexts'.freeze],
36
+ set_context: [:post, 'session/:session_id/context'.freeze],
37
+ app_strings: [:post, 'session/:session_id/appium/app/strings'.freeze],
38
+ lock: [:post, 'session/:session_id/appium/device/lock'.freeze],
39
+ install_app: [:post, 'session/:session_id/appium/device/install_app'.freeze],
40
+ remove_app: [:post, 'session/:session_id/appium/device/remove_app'.freeze],
41
+ app_installed?: [:post, 'session/:session_id/appium/device/app_installed'.freeze],
42
+ background_app: [:post, 'session/:session_id/appium/app/background'.freeze],
43
+ hide_keyboard: [:post, 'session/:session_id/appium/device/hide_keyboard'.freeze],
44
+ press_keycode: [:post, 'session/:session_id/appium/device/press_keycode'.freeze],
45
+ long_press_keycode: [:post, 'session/:session_id/appium/device/long_press_keycode'.freeze],
46
+ set_immediate_value: [:post, 'session/:session_id/appium/element/:id/value'.freeze],
47
+ replace_value: [:post, 'session/:session_id/appium/element/:id/replace_value'.freeze],
48
+ push_file: [:post, 'session/:session_id/appium/device/push_file'.freeze],
49
+ pull_file: [:post, 'session/:session_id/appium/device/pull_file'.freeze],
50
+ pull_folder: [:post, 'session/:session_id/appium/device/pull_folder'.freeze],
51
+ get_settings: [:get, 'session/:session_id/appium/settings'.freeze],
52
+ update_settings: [:post, 'session/:session_id/appium/settings'.freeze],
53
+ touch_actions: [:post, 'session/:session_id/touch/perform'.freeze],
54
+ multi_touch: [:post, 'session/:session_id/touch/multi/perform'.freeze]
55
+ }.freeze
56
+
57
+ COMMAND_ANDROID = {
58
+ start_activity: [:post, 'session/:session_id/appium/device/start_activity'.freeze],
59
+ end_coverage: [:post, 'session/:session_id/appium/app/end_test_coverage'.freeze],
60
+ set_network_connection: [:post, 'session/:session_id/network_connection'.freeze],
61
+ get_performance_data: [:post, 'session/:session_id/appium/getPerformanceData'.freeze],
62
+ start_recording_screen: [:post, 'session/:session_id/appium/start_recording_screen'.freeze]
63
+ }.freeze
64
+
65
+ COMMAND_IOS = {
66
+ touch_id: [:post, 'session/:session_id/appium/simulator/touch_id'.freeze],
67
+ toggle_touch_id_enrollment: [:post, 'session/:session_id/appium/simulator/toggle_touch_id_enrollment'.freeze]
68
+ }.freeze
69
+
70
+ COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).merge(COMMAND_IOS)
71
+ .merge(COMMAND_NO_ARG).freeze
72
+
73
+ COMMANDS_EXTEND_OSS = COMMANDS.merge(::Appium::Core::Base::Commands::OSS).freeze
74
+ COMMANDS_EXTEND_W3C = COMMANDS.merge(::Appium::Core::Base::Commands::W3C).freeze
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,567 @@
1
+ require 'base64'
2
+
3
+ module Appium
4
+ module Core
5
+ module Device
6
+ extend Forwardable
7
+
8
+ ####
9
+ ## No argument
10
+ ####
11
+
12
+ # @!method current_activity
13
+ # Get current activity name
14
+ # @return [String] An activity name
15
+ #
16
+ # @example
17
+ #
18
+ # @driver.current_activity # '.ApiDemos'
19
+ #
20
+
21
+ # @!method current_package
22
+ # Get current package name
23
+ # @return [String] A package name
24
+ #
25
+ # @example
26
+ #
27
+ # @driver.current_package # 'com.example.android.apis'
28
+ #
29
+
30
+ # @!method get_system_bars
31
+ # Get system bar's information
32
+ # @return [String]
33
+ #
34
+ # @example
35
+ #
36
+ # @driver.get_system_bars
37
+ #
38
+
39
+ # @!method get_display_density
40
+ # Get connected device's density.
41
+ # @return [Integer] The size of density
42
+ #
43
+ # @example
44
+ #
45
+ # @driver.get_display_density # 320
46
+ #
47
+
48
+ # @!method is_keyboard_shown
49
+ # Get whether keyboard is displayed or not.
50
+ # @return [Bool] Return true if keyboard is shown. Return false if keyboard is hidden.
51
+ #
52
+ # @example
53
+ # @driver.is_keyboard_shown # false
54
+ #
55
+
56
+ # @!method launch_app
57
+ # Start the simulator and application configured with desired capabilities
58
+ #
59
+ # @example
60
+ #
61
+ # @driver.launch_app
62
+ #
63
+
64
+ # @!method reset
65
+ # Reset the device, relaunching the application.
66
+ #
67
+ # @example
68
+ #
69
+ # @driver.reset
70
+ #
71
+
72
+ # @!method shake
73
+ # Cause the device to shake
74
+ #
75
+ # @example
76
+ #
77
+ # @driver.shake
78
+ #
79
+
80
+ # @!method toggle_flight_mode
81
+ # Toggle flight mode on or off
82
+ #
83
+ # @example
84
+ #
85
+ # @driver.toggle_flight_mode
86
+ #
87
+
88
+ # @!method device_locked?
89
+ # Check current device status is weather locked or not
90
+ #
91
+ # @example
92
+ #
93
+ # @driver.device_locked?
94
+ #
95
+
96
+ # @!method get_network_connection
97
+ # Get the device network connection current status
98
+ # See set_network_connection method for return value
99
+
100
+ ####
101
+ ## With arguments
102
+ ####
103
+
104
+ # @!method app_strings(language = nil)
105
+ # Return the hash of all localization strings.
106
+ # @return [Hash]
107
+ #
108
+ # @example
109
+ #
110
+ # @driver.app_strings #=> "TransitionsTitle"=>"Transitions", "WebTitle"=>"Web"
111
+ #
112
+
113
+ # @!method background_app(duration = 0)
114
+ # Backgrounds the app for a set number of seconds.
115
+ # This is a blocking application
116
+ # @param [Integer] duration How many seconds to background the app for.
117
+ # @return [String]
118
+ #
119
+ # @example
120
+ #
121
+ # @driver.background_app
122
+ # @driver.background_app(5)
123
+ # @driver.background_app(-1) #=> the app never come back. https://github.com/appium/appium/issues/7741
124
+ #
125
+
126
+ # @!method hide_keyboard(close_key = nil, strategy = nil)
127
+ # Hide the onscreen keyboard
128
+ # @param [String] close_key The name of the key which closes the keyboard.
129
+ # Defaults to 'Done' for iOS(except for XCUITest).
130
+ # @param [Symbol] strategy The symbol of the strategy which closes the keyboard.
131
+ # XCUITest ignore this argument.
132
+ # Default for iOS is `:pressKey`. Default for Android is `:tapOutside`.
133
+ #
134
+ # @example
135
+ #
136
+ # @driver.hide_keyboard # Close a keyboard with the 'Done' button
137
+ # @driver.hide_keyboard('Finished') # Close a keyboard with the 'Finished' button
138
+ # @driver.hide_keyboard(nil, :tapOutside) # Close a keyboard with tapping out side of keyboard
139
+ #
140
+
141
+ # @!method press_keycode(key, metastate = nil)
142
+ # Press keycode on the device.
143
+ # http://developer.android.com/reference/android/view/KeyEvent.html
144
+ # @param [integer] key The key to press.
145
+ # @param [String] metastate The state the metakeys should be in when pressing the key.
146
+ #
147
+ # @example
148
+ #
149
+ # @driver.press_keycode 82
150
+ #
151
+
152
+ # @!method long_press_keycode(key, metastate = nil)
153
+ # Long press keycode on the device.
154
+ # http://developer.android.com/reference/android/view/KeyEvent.html
155
+ # @param [integer] key The key to long press.
156
+ # @param [String] metastate The state the metakeys should be in when long pressing the key.
157
+ #
158
+ # @example
159
+ #
160
+ # @driver.long_press_keycode 82
161
+ #
162
+
163
+ # @!method push_file(path, filedata)
164
+ # Place a file in a specific location on the device.
165
+ # @param [String] path The absolute path on the device to store data at.
166
+ # @param [String] filedata Raw file data to be sent to the device. Converted to base64 in the method.
167
+ #
168
+ # @example
169
+ #
170
+ # @driver.push_file "/file/to/path", data
171
+ #
172
+
173
+ # @!method pull_file(path)
174
+ # Retrieve a file from the device. This can retrieve an absolute path or
175
+ # a path relative to the installed app (iOS only).
176
+ # @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
177
+ #
178
+ # @example
179
+ #
180
+ # @driver.pull_file '/local/data/some/path' #=> Get the file at that path
181
+ # @driver.pull_file 'Shenanigans.app/some/file' #=> Get 'some/file' from the install location of Shenanigans.app
182
+ #
183
+
184
+ # @!method pull_folder(path)
185
+ # Retrieve a folder from the device.
186
+ # @param [String] path absolute path to the folder
187
+ #
188
+ # @example
189
+ #
190
+ # @driver.pull_folder '/data/local/tmp' #=> Get the folder at that path
191
+ #
192
+
193
+ # @!method get_settings
194
+ # Get appium Settings for current test session
195
+ #
196
+ # @example
197
+ #
198
+ # @driver.pull_folder '/data/local/tmp' #=> Get the folder at that path
199
+ #
200
+
201
+ # @!method update_settings(settings)
202
+ # Update Appium Settings for current test session
203
+ # @param [Hash] settings Settings to update, keys are settings, values to value to set each setting to
204
+ #
205
+ # @example
206
+ #
207
+ # @driver.update_settings('allowInvisibleElements': true)
208
+ #
209
+
210
+ # @!method set_immediate_value(element, *value)
211
+ # Set the value to element directly
212
+ #
213
+ # @example
214
+ #
215
+ # set_immediate_value element, 'hello'
216
+ #
217
+
218
+ # @!method replace_value(element, *value)
219
+ # Replace the value to element directly
220
+ #
221
+ # @example
222
+ #
223
+ # replace_value element, 'hello'
224
+ #
225
+
226
+ # @!method ime_activate(ime_name)
227
+ # Android only. Make an engine that is available active.
228
+ # @param [String] ime_name The IME owning the activity [required]
229
+ #
230
+ # @example
231
+ #
232
+ # ime_activate engine: 'com.android.inputmethod.latin/.LatinIME'
233
+ #
234
+
235
+ # @!method ime_available_engines
236
+ # Android only. List all available input engines on the machine.
237
+ #
238
+ # @example
239
+ #
240
+ # ime_available_engines #=> Get the list of IME installed in the target device
241
+ #
242
+
243
+ # @!method ime_active_engine
244
+ # Android only. Get the name of the active IME engine.
245
+ #
246
+ # @example
247
+ #
248
+ # ime_active_engine #=> Get the current active IME such as 'com.android.inputmethod.latin/.LatinIME'
249
+ #
250
+
251
+ # @!method ime_activated
252
+ # Android only. Indicates whether IME input is active at the moment (not if it is available).
253
+ #
254
+ # @example
255
+ #
256
+ # ime_activated #=> True if IME is activated
257
+ #
258
+
259
+ # @!method ime_deactivate
260
+ # Android only. De-activates the currently-active IME engine.
261
+ #
262
+ # @example
263
+ #
264
+ # ime_deactivate #=> Deactivate current IME engine
265
+ #
266
+
267
+ ####
268
+ ## class << self
269
+ ####
270
+
271
+ class << self
272
+ def extended(_mod)
273
+ extend_webdriver_with_forwardable
274
+
275
+ ::Appium::Core::Commands::COMMAND_NO_ARG.each_key do |method|
276
+ add_endpoint_method method
277
+ end
278
+
279
+ # call http://www.rubydoc.info/gems/selenium-webdriver/Selenium/WebDriver/DriverExtensions/HasRemoteStatus#remote_status-instance_method
280
+ add_endpoint_method(:remote_status) do
281
+ def remote_status
282
+ execute(:status, {}) || []
283
+ end
284
+ end
285
+
286
+ # TODO: Don't define selenium-side methods. We pick up from them.
287
+ # ::Appium::Core::Base::Commands::OSS.each_key do |method|
288
+ # add_endpoint_method method
289
+ # end
290
+
291
+ add_endpoint_method(:available_contexts) do
292
+ def available_contexts
293
+ # return empty array instead of nil on failure
294
+ execute(:available_contexts, {}) || []
295
+ end
296
+ end
297
+
298
+ add_endpoint_method(:app_strings) do
299
+ def app_strings(language = nil)
300
+ opts = language ? { language: language } : {}
301
+ execute :app_strings, {}, opts
302
+ end
303
+ end
304
+
305
+ add_endpoint_method(:lock) do
306
+ def lock(duration)
307
+ execute :lock, {}, seconds: duration
308
+ end
309
+ end
310
+
311
+ add_endpoint_method(:install_app) do
312
+ def install_app(path)
313
+ execute :install_app, {}, appPath: path
314
+ end
315
+ end
316
+
317
+ add_endpoint_method(:remove_app) do
318
+ def remove_app(id)
319
+ execute :remove_app, {}, appId: id
320
+ end
321
+ end
322
+
323
+ add_endpoint_method(:app_installed?) do
324
+ def app_installed?(app_id)
325
+ execute :app_installed?, {}, bundleId: app_id
326
+ end
327
+ end
328
+
329
+ add_endpoint_method(:background_app) do
330
+ def background_app(duration = 0)
331
+ execute :background_app, {}, seconds: duration
332
+ end
333
+ end
334
+
335
+ add_endpoint_method(:set_context) do
336
+ def set_context(context = null)
337
+ execute :set_context, {}, name: context
338
+ end
339
+ end
340
+
341
+ add_endpoint_method(:hide_keyboard) do
342
+ def hide_keyboard(close_key = nil, strategy = nil)
343
+ option = {}
344
+
345
+ option[:key] = close_key || 'Done' # default to Done key.
346
+ option[:strategy] = strategy || :pressKey # default to pressKey
347
+
348
+ execute :hide_keyboard, {}, option
349
+ end
350
+ end
351
+
352
+ add_endpoint_method(:press_keycode) do
353
+ def press_keycode(key, metastate = nil)
354
+ args = { keycode: key }
355
+ args[:metastate] = metastate if metastate
356
+ execute :press_keycode, {}, args
357
+ end
358
+ end
359
+
360
+ add_endpoint_method(:long_press_keycode) do
361
+ def long_press_keycode(key, metastate = nil)
362
+ args = { keycode: key }
363
+ args[:metastate] = metastate if metastate
364
+ execute :long_press_keycode, {}, args
365
+ end
366
+ end
367
+
368
+ add_endpoint_method(:set_immediate_value) do
369
+ def set_immediate_value(element, *value)
370
+ keys = ::Selenium::WebDriver::Keys.encode(value)
371
+ execute :set_immediate_value, { id: element.ref }, value: Array(keys)
372
+ end
373
+ end
374
+
375
+ add_endpoint_method(:replace_value) do
376
+ def replace_value(element, *value)
377
+ keys = ::Selenium::WebDriver::Keys.encode(value)
378
+ execute :replace_value, { id: element.ref }, value: Array(keys)
379
+ end
380
+ end
381
+
382
+ add_endpoint_method(:push_file) do
383
+ def push_file(path, filedata)
384
+ encoded_data = Base64.encode64 filedata
385
+ execute :push_file, {}, path: path, data: encoded_data
386
+ end
387
+ end
388
+
389
+ add_endpoint_method(:pull_file) do
390
+ def pull_file(path)
391
+ data = execute :pull_file, {}, path: path
392
+ Base64.decode64 data
393
+ end
394
+ end
395
+
396
+ add_endpoint_method(:pull_folder) do
397
+ def pull_folder(path)
398
+ data = execute :pull_folder, {}, path: path
399
+ Base64.decode64 data
400
+ end
401
+ end
402
+
403
+ add_endpoint_method(:get_settings) do
404
+ def get_settings
405
+ execute :get_settings, {}
406
+ end
407
+ end
408
+
409
+ add_endpoint_method(:update_settings) do
410
+ def update_settings(settings)
411
+ execute :update_settings, {}, settings: settings
412
+ end
413
+ end
414
+
415
+ add_touch_actions
416
+ add_ime_actions
417
+ end
418
+
419
+ # def extended
420
+
421
+ # @private
422
+ def add_endpoint_method(method)
423
+ block_given? ? create_bridge_command(method, &Proc.new) : create_bridge_command(method)
424
+
425
+ delegate_driver_method method
426
+ delegate_from_appium_driver method
427
+ end
428
+
429
+ # @private CoreBridge
430
+ def extend_webdriver_with_forwardable
431
+ return if ::Appium::Core::Base::Driver.is_a? Forwardable
432
+ ::Appium::Core::Base::Driver.class_eval do
433
+ extend Forwardable
434
+ end
435
+ end
436
+
437
+ # @private
438
+ def delegate_driver_method(method)
439
+ return if ::Appium::Core::Base::Driver.method_defined? method
440
+ ::Appium::Core::Base::Driver.class_eval { def_delegator :@bridge, method }
441
+ end
442
+
443
+ # @private
444
+ def delegate_from_appium_driver(method, delegation_target = :driver)
445
+ def_delegator delegation_target, method
446
+ end
447
+
448
+ # @private
449
+ def create_bridge_command(method)
450
+ ::Appium::Core::Base::CoreBridgeOSS.class_eval do
451
+ block_given? ? class_eval(&Proc.new) : define_method(method) { execute method }
452
+ end
453
+ ::Appium::Core::Base::CoreBridgeW3C.class_eval do
454
+ block_given? ? class_eval(&Proc.new) : define_method(method) { execute method }
455
+ end
456
+ end
457
+
458
+ def add_touch_actions
459
+ add_endpoint_method(:touch_actions) do
460
+ def touch_actions(actions)
461
+ actions = { actions: [actions].flatten }
462
+ execute :touch_actions, {}, actions
463
+ end
464
+ end
465
+
466
+ add_endpoint_method(:multi_touch) do
467
+ def multi_touch(actions)
468
+ execute :multi_touch, {}, actions: actions
469
+ end
470
+ end
471
+ end
472
+
473
+ def add_ime_actions
474
+ add_endpoint_method(:ime_activate) do
475
+ def ime_activate(ime_name)
476
+ # from Selenium::WebDriver::Remote::OSS
477
+ execute :ime_activate_engine, {}, engine: ime_name
478
+ end
479
+ end
480
+
481
+ add_endpoint_method(:ime_available_engines) do
482
+ def ime_available_engines
483
+ execute :ime_get_available_engines
484
+ end
485
+ end
486
+
487
+ add_endpoint_method(:ime_active_engine) do
488
+ # from Selenium::WebDriver::Remote::OSS
489
+ def ime_active_engine
490
+ execute :ime_get_active_engine
491
+ end
492
+ end
493
+
494
+ add_endpoint_method(:ime_activated) do
495
+ # from Selenium::WebDriver::Remote::OSS
496
+ def ime_activated
497
+ execute :ime_is_activated
498
+ end
499
+ end
500
+
501
+ add_endpoint_method(:ime_deactivate) do
502
+ # from Selenium::WebDriver::Remote::OSS
503
+ def ime_deactivate
504
+ execute :ime_deactivate, {}
505
+ end
506
+ end
507
+ end
508
+ end # class << self
509
+
510
+ # @!method set_context(context)
511
+ # Change the context to the given context.
512
+ # @param [String] context The context to change to
513
+ #
514
+ # @example
515
+ #
516
+ # @driver.set_context "NATIVE_APP"
517
+ #
518
+
519
+ # @!method current_context
520
+ # @return [String] The context currently being used.
521
+ #
522
+ # @example
523
+ #
524
+ # @driver.current_context
525
+ #
526
+
527
+ # @!method available_contexts
528
+ # @return [Array<String>] All usable contexts, as an array of strings.
529
+ #
530
+ # @example
531
+ #
532
+ # @driver.available_contexts
533
+ #
534
+
535
+ # Perform a block within the given context, then switch back to the starting context.
536
+ # @param [String] context The context to switch to for the duration of the block.
537
+ #
538
+ # @example
539
+ #
540
+ # result = @driver.within_context('NATIVE_APP') do
541
+ # @driver.find_element :tag, "button"
542
+ # end # The result of `find_element :tag, "button"`
543
+ #
544
+ def within_context(context)
545
+ existing_context = current_context
546
+ set_context context
547
+ if block_given?
548
+ result = yield
549
+ set_context existing_context
550
+ result
551
+ else
552
+ set_context existing_context
553
+ end
554
+ end
555
+
556
+ # Change to the default context. This is equivalent to `set_context nil`.
557
+ #
558
+ # @example
559
+ #
560
+ # @driver.switch_to_default_context
561
+ #
562
+ def switch_to_default_context
563
+ set_context nil
564
+ end
565
+ end # module Device
566
+ end # module Core
567
+ end # module Appium