appium_lib 0.24.1 → 1.0.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +17 -8
  3. data/android_tests/Gemfile +1 -0
  4. data/android_tests/LICENSE-2.0.txt +202 -0
  5. data/android_tests/Rakefile +61 -0
  6. data/android_tests/api.apk +0 -0
  7. data/android_tests/appium.txt +3 -0
  8. data/android_tests/flaky.txt +1 -0
  9. data/android_tests/lib/android/specs/android/dynamic.rb +5 -0
  10. data/android_tests/lib/android/specs/android/element/alert.rb +41 -0
  11. data/android_tests/lib/android/specs/android/element/button.rb +55 -0
  12. data/android_tests/lib/android/specs/android/element/generic.rb +48 -0
  13. data/android_tests/lib/android/specs/android/element/text.rb +39 -0
  14. data/android_tests/lib/android/specs/android/element/textfield.rb +60 -0
  15. data/android_tests/lib/android/specs/android/helper.rb +80 -0
  16. data/android_tests/lib/android/specs/android/patch.rb +14 -0
  17. data/android_tests/lib/android/specs/common/device.rb +117 -0
  18. data/android_tests/lib/android/specs/common/element/window.rb +9 -0
  19. data/android_tests/lib/android/specs/common/helper.rb +112 -0
  20. data/android_tests/lib/android/specs/common/patch.rb +69 -0
  21. data/android_tests/lib/android/specs/common/version.rb +9 -0
  22. data/android_tests/lib/android/specs/driver.rb +174 -0
  23. data/android_tests/lib/format.rb +49 -0
  24. data/android_tests/lib/run.rb +72 -0
  25. data/android_tests/readme.md +27 -0
  26. data/appium_lib.gemspec +8 -5
  27. data/docs/android_docs.md +1052 -716
  28. data/docs/ios_docs.md +657 -834
  29. data/docs_gen/make_docs.rb +1 -3
  30. data/ios_tests/Gemfile +1 -0
  31. data/ios_tests/LICENSE-2.0.txt +202 -0
  32. data/ios_tests/Rakefile +47 -0
  33. data/ios_tests/UICatalog.app.zip +0 -0
  34. data/ios_tests/UICatalog.app/12-6AM.png +0 -0
  35. data/ios_tests/UICatalog.app/12-6PM.png +0 -0
  36. data/ios_tests/UICatalog.app/6-12AM.png +0 -0
  37. data/ios_tests/UICatalog.app/6-12PM.png +0 -0
  38. data/ios_tests/UICatalog.app/Default-568h@2x.png +0 -0
  39. data/ios_tests/UICatalog.app/Default@2x.png +0 -0
  40. data/ios_tests/UICatalog.app/Info.plist +0 -0
  41. data/ios_tests/UICatalog.app/PkgInfo +1 -0
  42. data/ios_tests/UICatalog.app/UIButton_custom.png +0 -0
  43. data/ios_tests/UICatalog.app/UICatalog +0 -0
  44. data/ios_tests/UICatalog.app/blueButton.png +0 -0
  45. data/ios_tests/UICatalog.app/bookmarkImage.png +0 -0
  46. data/ios_tests/UICatalog.app/bookmarkImageHighlighted.png +0 -0
  47. data/ios_tests/UICatalog.app/divider.png +0 -0
  48. data/ios_tests/UICatalog.app/en.lproj/AlertsViewController.nib +0 -0
  49. data/ios_tests/UICatalog.app/en.lproj/ButtonsViewController.nib +0 -0
  50. data/ios_tests/UICatalog.app/en.lproj/ControlsViewController.nib +0 -0
  51. data/ios_tests/UICatalog.app/en.lproj/ImagesViewController.nib +0 -0
  52. data/ios_tests/UICatalog.app/en.lproj/Localizable.strings +0 -0
  53. data/ios_tests/UICatalog.app/en.lproj/MainWindow.nib +0 -0
  54. data/ios_tests/UICatalog.app/en.lproj/PickerViewController.nib +0 -0
  55. data/ios_tests/UICatalog.app/en.lproj/SearchBarController.nib +0 -0
  56. data/ios_tests/UICatalog.app/en.lproj/SegmentViewController.nib +0 -0
  57. data/ios_tests/UICatalog.app/en.lproj/TextFieldController.nib +0 -0
  58. data/ios_tests/UICatalog.app/en.lproj/TextViewController.nib +0 -0
  59. data/ios_tests/UICatalog.app/en.lproj/ToolbarViewController.nib +0 -0
  60. data/ios_tests/UICatalog.app/en.lproj/TransitionViewController.nib +0 -0
  61. data/ios_tests/UICatalog.app/en.lproj/WebViewController.nib +0 -0
  62. data/ios_tests/UICatalog.app/orangeslide.png +0 -0
  63. data/ios_tests/UICatalog.app/scene1.jpg +0 -0
  64. data/ios_tests/UICatalog.app/scene2.jpg +0 -0
  65. data/ios_tests/UICatalog.app/scene3.jpg +0 -0
  66. data/ios_tests/UICatalog.app/scene4.jpg +0 -0
  67. data/ios_tests/UICatalog.app/scene5.jpg +0 -0
  68. data/ios_tests/UICatalog.app/searchBarBackground.png +0 -0
  69. data/ios_tests/UICatalog.app/segment_check.png +0 -0
  70. data/ios_tests/UICatalog.app/segment_search.png +0 -0
  71. data/ios_tests/UICatalog.app/segment_tools.png +0 -0
  72. data/ios_tests/UICatalog.app/segmentedBackground.png +0 -0
  73. data/ios_tests/UICatalog.app/slider_ball.png +0 -0
  74. data/ios_tests/UICatalog.app/toolbarBackground.png +0 -0
  75. data/ios_tests/UICatalog.app/whiteButton.png +0 -0
  76. data/ios_tests/UICatalog.app/yellowslide.png +0 -0
  77. data/ios_tests/appium.txt +3 -0
  78. data/ios_tests/flaky.txt +1 -0
  79. data/ios_tests/lib/format.rb +25 -0
  80. data/ios_tests/lib/ios/specs/common/element/window.rb +15 -0
  81. data/ios_tests/lib/ios/specs/common/helper.rb +204 -0
  82. data/ios_tests/lib/ios/specs/common/patch.rb +50 -0
  83. data/ios_tests/lib/ios/specs/common/version.rb +17 -0
  84. data/ios_tests/lib/ios/specs/device/device.rb +82 -0
  85. data/ios_tests/lib/ios/specs/device/multi_touch.rb +12 -0
  86. data/ios_tests/lib/ios/specs/device/touch_actions.rb +15 -0
  87. data/ios_tests/lib/ios/specs/driver.rb +203 -0
  88. data/ios_tests/lib/ios/specs/ios/element/alert.rb +48 -0
  89. data/ios_tests/lib/ios/specs/ios/element/button.rb +58 -0
  90. data/ios_tests/lib/ios/specs/ios/element/generic.rb +35 -0
  91. data/ios_tests/lib/ios/specs/ios/element/text.rb +54 -0
  92. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +123 -0
  93. data/ios_tests/lib/ios/specs/ios/helper.rb +27 -0
  94. data/ios_tests/lib/ios/specs/ios/patch.rb +30 -0
  95. data/ios_tests/lib/run.rb +106 -0
  96. data/ios_tests/readme.md +30 -0
  97. data/ios_tests/upload/sauce_storage.rb +64 -0
  98. data/ios_tests/upload/upload.rb +6 -0
  99. data/lib/appium_lib.rb +4 -14
  100. data/lib/appium_lib/android/dynamic.rb +30 -32
  101. data/lib/appium_lib/android/element/alert.rb +34 -33
  102. data/lib/appium_lib/android/element/button.rb +91 -0
  103. data/lib/appium_lib/android/element/generic.rb +51 -146
  104. data/lib/appium_lib/android/element/text.rb +54 -0
  105. data/lib/appium_lib/android/element/textfield.rb +46 -41
  106. data/lib/appium_lib/android/helper.rb +248 -417
  107. data/lib/appium_lib/android/mobile_methods.rb +17 -0
  108. data/lib/appium_lib/android/patch.rb +9 -8
  109. data/lib/appium_lib/awesome_print/ostruct.rb +33 -0
  110. data/lib/appium_lib/common/element/window.rb +9 -8
  111. data/lib/appium_lib/common/helper.rb +182 -243
  112. data/lib/appium_lib/common/patch.rb +65 -79
  113. data/lib/appium_lib/common/version.rb +2 -3
  114. data/lib/appium_lib/device/device.rb +339 -0
  115. data/lib/appium_lib/device/multi_touch.rb +94 -0
  116. data/lib/appium_lib/device/touch_actions.rb +142 -0
  117. data/lib/appium_lib/driver.rb +217 -306
  118. data/lib/appium_lib/ios/element/alert.rb +16 -92
  119. data/lib/appium_lib/ios/element/button.rb +55 -0
  120. data/lib/appium_lib/ios/element/generic.rb +27 -160
  121. data/lib/appium_lib/ios/element/text.rb +54 -0
  122. data/lib/appium_lib/ios/element/textfield.rb +78 -65
  123. data/lib/appium_lib/ios/helper.rb +300 -190
  124. data/lib/appium_lib/ios/mobile_methods.rb +17 -0
  125. data/lib/appium_lib/ios/patch.rb +55 -41
  126. data/lib/appium_lib/logger.rb +13 -0
  127. data/lib/appium_lib/rails/duplicable.rb +116 -0
  128. data/readme.md +6 -1
  129. data/release_notes.md +118 -0
  130. metadata +170 -12
  131. data/lib/appium_lib/common/element/button.rb +0 -83
  132. data/lib/appium_lib/common/element/text.rb +0 -61
@@ -1,71 +1,50 @@
1
- # encoding: utf-8
2
-
3
- # Add status to WebDriver
4
- # https://code.google.com/p/selenium/issues/detail?id=5669
5
- class Selenium::WebDriver::Driver
6
- # @private
7
- def status
8
- bridge.status
9
- end
10
- end
11
-
12
- class Selenium::WebDriver::Remote::Bridge
13
- # @private
14
- def status
15
- raw_execute :status
16
- end
17
- end
18
-
19
- class Selenium::WebDriver::Remote::Bridge
20
- command :status, :get, 'status'
21
- end
22
- # end Add status to WebDriver
23
-
24
- module Appium::Common
25
- # Implement useful features for element.
26
- class Selenium::WebDriver::Element
27
- # Note: For testing .text should be used over value, and name.
28
-
29
- # Returns the value attribute
30
- #
31
- # Fixes NoMethodError: undefined method `value' for Selenium::WebDriver::Element
32
- def value
33
- self.attribute :value
34
- end
1
+ module Appium
2
+ module Common
3
+ # Implement useful features for element.
4
+ class Selenium::WebDriver::Element
5
+ # Note: For testing .text should be used over value, and name.
6
+
7
+ # Returns the value attribute
8
+ #
9
+ # Fixes NoMethodError: undefined method `value' for Selenium::WebDriver::Element
10
+ def value
11
+ self.attribute :value
12
+ end
35
13
 
36
- # Returns the name attribute
37
- #
38
- # Fixes NoMethodError: undefined method `name' for Selenium::WebDriver::Element
39
- def name
40
- self.attribute :name
41
- end
14
+ # Returns the name attribute
15
+ #
16
+ # Fixes NoMethodError: undefined method `name' for Selenium::WebDriver::Element
17
+ def name
18
+ self.attribute :name
19
+ end
42
20
 
43
- # For use with mobile tap.
44
- #
45
- # ```ruby
46
- # execute_script 'mobile: tap', :x => 0.0, :y => 0.98
47
- # ```
48
- #
49
- # https://github.com/appium/appium/wiki/Automating-mobile-gestures
50
- # @return [OpenStruct] the relative x, y in a struct. ex: { x: 0.50, y: 0.20 }
51
- def location_rel
52
- location = self.location
53
- location_x = location.x.to_f
54
- location_y = location.y.to_f
55
-
56
- size = self.size
57
- size_width = size.width.to_f
58
- size_height = size.height.to_f
59
-
60
- center_x = location_x + (size_width / 2.0)
61
- center_y = location_y + (size_height / 2.0)
62
-
63
- w = $driver.window_size
64
- OpenStruct.new( x: "#{center_x} / #{w.width.to_f}",
65
- y: "#{center_y} / #{w.height.to_f}" )
21
+ # For use with mobile tap.
22
+ #
23
+ # ```ruby
24
+ # execute_script 'mobile: tap', :x => 0.0, :y => 0.98
25
+ # ```
26
+ #
27
+ # https://github.com/appium/appium/wiki/Automating-mobile-gestures
28
+ # @return [OpenStruct] the relative x, y in a struct. ex: { x: 0.50, y: 0.20 }
29
+ def location_rel
30
+ location = self.location
31
+ location_x = location.x.to_f
32
+ location_y = location.y.to_f
33
+
34
+ size = self.size
35
+ size_width = size.width.to_f
36
+ size_height = size.height.to_f
37
+
38
+ center_x = location_x + (size_width / 2.0)
39
+ center_y = location_y + (size_height / 2.0)
40
+
41
+ w = $driver.window_size
42
+ OpenStruct.new(x: "#{center_x} / #{w.width.to_f}",
43
+ y: "#{center_y} / #{w.height.to_f}")
44
+ end
66
45
  end
67
- end
68
- end # module Appium::Common
46
+ end # module Common
47
+ end # module Appium
69
48
 
70
49
  # Print JSON posted to Appium. Not scoped to an Appium module.
71
50
  #
@@ -86,14 +65,14 @@ def patch_webdriver_bridge
86
65
  Selenium::WebDriver::Remote::Bridge.class_eval do
87
66
  # Code from lib/selenium/webdriver/remote/bridge.rb
88
67
  def raw_execute(command, opts = {}, command_hash = nil)
89
- verb, path = Selenium::WebDriver::Remote::COMMANDS[command] || raise(ArgumentError, "unknown command: #{command.inspect}")
90
- path = path.dup
68
+ verb, path = Selenium::WebDriver::Remote::COMMANDS[command] || raise(ArgumentError, "unknown command: #{command.inspect}")
69
+ path = path.dup
91
70
 
92
71
  path[':session_id'] = @session_id if path.include?(':session_id')
93
72
 
94
73
  begin
95
74
  opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
96
- rescue IndexError
75
+ rescue IndexError
97
76
  raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
98
77
  end
99
78
 
@@ -103,39 +82,46 @@ def patch_webdriver_bridge
103
82
  # change path from session/efac972c-941a-499c-803c-d7d008749/execute
104
83
  # to /execute
105
84
  # path may be nil, session, or not have anything after the session_id.
106
- path_str = path
107
- path_str = '/' + path_str unless path_str.nil? ||
85
+ path_str = path
86
+ path_str = '/' + path_str unless path_str.nil? ||
108
87
  path_str.length <= 0 || path_str[0] == '/'
109
88
  path_match = path.match /.*\h{8}-?\h{4}-?\h{4}-?\h{4}-?\h{12}/
110
- path_str = path.sub(path_match[0], '') unless path_match.nil?
89
+ path_str = path.sub(path_match[0], '') unless path_match.nil?
111
90
 
112
91
  puts "#{verb} #{path_str}"
113
92
  unless command_hash.nil? || command_hash.length == 0
114
- print_command = command_hash.clone
93
+ print_command = {}
94
+ # For complex_find, we pass a whole array
95
+ if command_hash.kind_of? Array
96
+ print_command[:args] = [command_hash.clone]
97
+ print_command[:script] = 'mobile: find' if command == :complex_find
98
+ else
99
+ print_command = command_hash.clone
100
+ end
115
101
  print_command.delete :args if print_command[:args] == []
116
102
 
117
103
  mobile_find = 'mobile: find'
118
104
  if print_command[:script] == mobile_find
119
105
  args = print_command[:args]
120
- puts "#{mobile_find}"#" #{args}"
106
+ puts "#{mobile_find}" #" #{args}"
121
107
 
122
108
  # [[[[3, "sign"]]]] => [[[3, "sign"]]]
123
109
  #
124
110
  # [[[[4, "android.widget.EditText"], [7, "z"]], [[4, "android.widget.EditText"], [3, "z"]]]]
125
111
  # => [[[4, "android.widget.EditText"], [7, "z"]], [[4, "android.widget.EditText"], [3, "z"]]]
126
- args = args[0]
127
- option = args[0].to_s.downcase
128
- has_option = ! option.match(/all|scroll/).nil?
112
+ args = args[0]
113
+ option = args[0].to_s.downcase
114
+ has_option = !option.match(/all|scroll/).nil?
129
115
  puts option if has_option
130
116
 
131
117
  start = has_option ? 1 : 0
132
118
 
133
119
  start.upto(args.length-1) do |selector_index|
134
- selectors = args[selector_index]
120
+ selectors = args[selector_index]
135
121
  selectors_size = selectors.length
136
122
  selectors.each_index do |pair_index|
137
123
  pair = selectors[pair_index]
138
- res = $driver.dynamic_code_to_string pair[0], pair[1]
124
+ res = $driver.dynamic_code_to_string pair[0], pair[1]
139
125
 
140
126
  if selectors_size == 1 || pair_index >= selectors_size - 1
141
127
  puts res
@@ -154,7 +140,7 @@ def patch_webdriver_bridge
154
140
  http.call verb, path, command_hash
155
141
  end # def
156
142
  end # class
157
- end # def
143
+ end
158
144
 
159
145
  # Print Appium's origValue error messages.
160
146
  class Selenium::WebDriver::Remote::Response
@@ -1,6 +1,5 @@
1
- # encoding: utf-8
2
1
  module Appium
3
2
  # Version and Date are defined on the 'Appium' module, not 'Appium::Common'
4
- VERSION = '0.24.1' unless defined? ::Appium::VERSION
5
- DATE = '2014-03-26' unless defined? ::Appium::DATE
3
+ VERSION = '1.0.0' unless defined? ::Appium::VERSION
4
+ DATE = '2014-04-29' unless defined? ::Appium::DATE
6
5
  end
@@ -0,0 +1,339 @@
1
+ require 'base64'
2
+
3
+ module Appium
4
+ module Device
5
+ extend Forwardable
6
+
7
+ NoArgMethods = {
8
+ post: {
9
+ shake: 'session/:session_id/appium/device/shake',
10
+ launch: 'session/:session_id/appium/app/launch',
11
+ close_app: 'session/:session_id/appium/app/close',
12
+ reset: 'session/:session_id/appium/app/reset',
13
+ toggle_airplane_mode: 'session/:session_id/appium/device/toggle_airplane_mode'
14
+ },
15
+ get: {
16
+ current_activity: 'session/:session_id/appium/device/current_activity',
17
+ current_context: 'session/:session_id/context',
18
+ app_strings: 'session/:session_id/appium/app/strings',
19
+ available_contexts: 'session/:session_id/contexts',
20
+ }
21
+ }
22
+
23
+ # @!method app_strings
24
+ # Return the hash of all localization strings.
25
+ # ```ruby
26
+ # app_strings #=> "TransitionsTitle"=>"Transitions", "WebTitle"=>"Web"
27
+ # ```
28
+
29
+ # @!method background_app
30
+ # Backgrounds the app for a set number of seconds.
31
+ # This is a blocking application
32
+ # @param seconds (int) How many seconds to background the app for.
33
+
34
+ # @!method current_activity
35
+
36
+ # @!method launch
37
+ # Start the simulator and applicaton configured with desired capabilities
38
+
39
+ # @!method reset
40
+ # Reset the device, relaunching the application.
41
+
42
+ # @!method shake
43
+ # Cause the device to shake
44
+
45
+ # @!method toggle_flight_mode
46
+ # toggle flight mode on or off
47
+
48
+ #@!method complex_find
49
+ # Find an element by a complex array of criteria. Available criteria
50
+ # are listed in [link here]. Criteria are formed by creating an array
51
+ # of arrays, each containing a selector and that selector's value.
52
+ #
53
+ # ```ruby
54
+ # complex_find [[[2, 'Sau'], [14, true]]] # => Find a clickable element
55
+ # # whose names starts with 'Sau'
56
+ # ```
57
+ # @param mod (Symbol) If present, will be the 0th element in the selector array.
58
+ # @param selectors (Array<Object>) The selectors to find elements with.
59
+
60
+ # @!method hide_keyboard
61
+ # Hide the onscreen keyboard
62
+ # @param close_key (String) the name of the key which closes the keyboard.
63
+ # Defaults to 'Done'.
64
+ # ```ruby
65
+ # hide_keyboard # Close a keyboard with the 'Done' button
66
+ # hide_keyboard('Finished') # Close a keyboard with the 'Finished' button
67
+ # ```
68
+
69
+ # @!method key_event
70
+ # Send a key event to the device.
71
+ # @param key (integer) The key to send.
72
+ # @param metastate (String) The state the metakeys should be in when sending the key.
73
+
74
+ # @!method push_file
75
+ # Place a file in a specific location on the device.
76
+ # @param path (String) The absolute path on the device to store data at.
77
+ # @param data (String) Raw file data to be sent to the device.
78
+
79
+ # @!method pull_file
80
+ # Retrieve a file from the device. This can retrieve an absolute path or
81
+ # a path relative to the installed app (iOS only).
82
+ # @param path (String) Either an absolute path OR, for iOS devices, a path relative to the app, as described.
83
+ #
84
+ # ```ruby
85
+ # pull_file '/local/data/some/path' #=> Get the file at that path
86
+ # pull_file 'Shenanigans.app/some/file' #=> Get 'some/file' from the install location of Shenanigans.app
87
+ # ```
88
+
89
+ # @!method end_coverage
90
+ # Android only; Ends the test coverage and writes the results to the given path on device.
91
+ # @param path (String) Path on the device to write too.
92
+ # @param intent (String) Intent to broadcast when ending coverage.
93
+ class << self
94
+ def extended(mod)
95
+ extend_webdriver_with_forwardable
96
+
97
+ NoArgMethods.each_pair do |verb, pair|
98
+ pair.each_pair { |command, path| add_endpoint_method command, path, verb }
99
+ end
100
+
101
+ add_endpoint_method(:lock, 'session/:session_id/appium/device/lock') do
102
+ def lock(duration)
103
+ execute :lock, {}, :seconds => duration
104
+ end
105
+ end
106
+
107
+ add_endpoint_method(:install, 'session/:session_id/appium/device/install_app') do
108
+ def install(path)
109
+ execute :install, {}, :appPath => path
110
+ end
111
+ end
112
+
113
+ add_endpoint_method(:remove, 'session/:session_id/appium/device/remove_app') do
114
+ def remove(id)
115
+ execute :remove, {}, :appId => id
116
+ end
117
+ end
118
+
119
+ add_endpoint_method(:is_installed?, 'session/:session_id/appium/device/app_installed') do
120
+ def is_installed?(app_id)
121
+ execute :is_installed?, {}, :bundleId => app_id
122
+ end
123
+ end
124
+
125
+ add_endpoint_method(:background_app, 'session/:session_id/appium/app/background') do
126
+ def background_app(duration)
127
+ execute :background_app, {}, :seconds => duration
128
+ end
129
+ end
130
+
131
+ add_endpoint_method(:current_context=, 'session/:session_id/context') do
132
+ def current_context=(context=null)
133
+ execute :current_context=, {}, :context => context
134
+ end
135
+ end
136
+
137
+ add_endpoint_method(:hide_keyboard, 'session/:session_id/appium/device/hide_keyboard') do
138
+ def hide_keyboard(close_key='Done')
139
+ execute :hide_keyboard, {}, keyName: close_key
140
+ end
141
+ end
142
+
143
+ add_endpoint_method(:key_event, 'session/:session_id/appium/device/keyevent') do
144
+ def key_event(key, metastate=nil)
145
+ args = {keycode: key}
146
+ args[:metastate] = metastate if metastate
147
+ execute :key_event, {}, args
148
+ end
149
+ end
150
+
151
+ # TODO TEST ME
152
+ add_endpoint_method(:set_immediate_value, 'session/:session_id/appium/element/:id/value') do
153
+ def set_immediate_value(element, value)
154
+ execute :set_immediate_value, { :id => element.ref }, value
155
+ end
156
+ end
157
+
158
+ add_endpoint_method(:complex_find, 'session/:session_id/appium/app/complex_find') do
159
+ def complex_find(opts)
160
+ mode = opts.fetch :mode, false
161
+ selectors = opts.fetch :selectors, false
162
+ raise 'Complex find must have selectors' unless selectors
163
+ selectors.insert(0, mode) if mode
164
+
165
+ ids = execute :complex_find, {}, selectors
166
+
167
+ if mode == 'all' && ids.length > 1
168
+ return ids.map { |id| Selenium::WebDriver::Element.new self, element_id_from(id) }
169
+ else
170
+ return Selenium::WebDriver::Element.new self, element_id_from(ids)
171
+ end
172
+ end
173
+ end
174
+
175
+ add_endpoint_method(:push_file,'session/:session_id/appium/device/push_file') do
176
+ def push_file(path, filedata)
177
+ encoded_data = Base64.encode64 filedata
178
+ execute :push_file, {}, path: path, data: encoded_data
179
+ end
180
+ end
181
+
182
+ add_endpoint_method(:pull_file, 'session/:session_id/appium/device/pull_file') do
183
+ def pull_file(path)
184
+ data = execute :pull_file, {}, path: path
185
+ Base64.decode64 data
186
+ end
187
+ end
188
+
189
+ # TODO TEST ME
190
+ add_endpoint_method(:end_coverage, 'session/:session_id/appium/app/end_test_coverage') do
191
+ def end_coverage(path, intent)
192
+ execute :end_coverage, {}, path: path, intent: intent
193
+ end
194
+ end
195
+
196
+ add_touch_actions
197
+ extend_search_contexts
198
+ end
199
+
200
+ # def extended
201
+
202
+ # @private
203
+ def add_endpoint_method(method, path, verb=:post)
204
+ if block_given?
205
+ # &Proc.new with no args passes the passed_in block
206
+ # Because creating Procs from blocks is slow
207
+ create_bridge_command method, verb, path, &Proc.new
208
+ else
209
+ create_bridge_command method, verb, path
210
+ end
211
+
212
+ delegate_driver_method method
213
+ delegate_from_appium_driver method
214
+ end
215
+
216
+ # @private
217
+ def extend_webdriver_with_forwardable
218
+ return if Selenium::WebDriver::Driver.kind_of? Forwardable
219
+ Selenium::WebDriver::Driver.class_eval do
220
+ extend Forwardable
221
+ end
222
+ end
223
+
224
+ # @private
225
+ def delegate_driver_method(method)
226
+ return if Selenium::WebDriver::Driver.method_defined? method
227
+ Selenium::WebDriver::Driver.class_eval { def_delegator :@bridge, method }
228
+ end
229
+
230
+ # @private
231
+ def delegate_from_appium_driver(method, delegation_target=:driver)
232
+ def_delegator delegation_target, method
233
+ end
234
+
235
+ # @private
236
+ def create_bridge_command(method, verb, path)
237
+ # Don't clobber methods that are moved into Selenium
238
+ if selenium_has method
239
+ log_reimplemented_warning(method, path)
240
+ return
241
+ end
242
+
243
+ Selenium::WebDriver::Remote::Bridge.class_eval do
244
+ command method, verb, path
245
+ if block_given?
246
+ class_eval &Proc.new
247
+ else
248
+ define_method(method) { execute method }
249
+ end
250
+ end
251
+ end
252
+
253
+ # @private
254
+ def selenium_has(method)
255
+ Selenium::WebDriver::Remote::Bridge.method_defined? method
256
+ end
257
+
258
+ # @private
259
+ def log_reimplemented_warning(method, path)
260
+ msg = "Selenium::WebDriver has now implemented the `#{method}` method."
261
+ if Selenium::WebDriver::Remote::COMMANDS[method][1] == path
262
+ msg << " It may no longer function as expected"
263
+ else
264
+ msg << " It no longer uses the same endpoint,"
265
+ msg << " so it probably won't do what you expect anymore."
266
+ end
267
+ msg << " Raise an issue at http://www.github.com/appium/ruby_lib if so."
268
+ Appium::Logger.warn msg
269
+ end
270
+
271
+ # @!method accessiblity_id_find
272
+ # find_element/s with their accessibility_id
273
+ #
274
+ # ```ruby
275
+ # find_elements :accessibility_id, 'Animation'
276
+ # ```
277
+ def extend_search_contexts
278
+ Selenium::WebDriver::SearchContext.class_eval do
279
+ Selenium::WebDriver::SearchContext::FINDERS[:accessibility_id] = 'accessibility id'
280
+ end
281
+ end
282
+
283
+ def add_touch_actions
284
+ add_endpoint_method(:touch_actions, 'session/:session_id/touch/perform') do
285
+ def touch_actions(actions)
286
+ actions = [actions].flatten
287
+ execute :touch_actions, {}, actions
288
+ end
289
+ end
290
+
291
+ add_endpoint_method(:multi_touch, 'session/:session_id/touch/multi/perform') do
292
+ def multi_touch(actions)
293
+ execute :multi_touch, {}, actions: actions
294
+ end
295
+ end
296
+
297
+ actions = Appium::TouchAction::COMPLEX_ACTIONS
298
+ actions.each do |method|
299
+ delegate_from_appium_driver(method, Appium::TouchAction)
300
+ end
301
+
302
+ delegate_from_appium_driver(:pinch, Appium::MultiTouch)
303
+ delegate_from_appium_driver(:zoom, Appium::MultiTouch)
304
+ end
305
+ end # class << self
306
+
307
+ # @!method current_context=
308
+ # Change the context to the given context.
309
+ # @param [String] The context to change to
310
+ #
311
+ # ```ruby
312
+ # current_context= "NATIVE_APP"
313
+ # ```
314
+
315
+ # @!method current_context
316
+ # @return [String] The context currently being used.
317
+
318
+ # @!method available_contexts
319
+ # @return [Array<String>] All usable contexts, as an array of strings.
320
+
321
+ # Perform a block within the given context, then switch back to the starting context.
322
+ # @param context (String) The context to switch to for the duration of the block.
323
+ #
324
+ # ```ruby
325
+ # within_context('NATIVE_APP') do
326
+ # find_element [:tag, "button"]
327
+ # ```
328
+ def within_context(context)
329
+ existing_context = current_context
330
+ yield if block_given?
331
+ current_context = existing_context
332
+ end
333
+
334
+ # Change to the default context. This is equivalent to `current_context= nil`.
335
+ def switch_to_default_context
336
+ current_context = nil
337
+ end
338
+ end # module Device
339
+ end # module Appium