appium_lib 0.24.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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