appium_lib 4.1.0 → 5.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/android_tests/api.apk +0 -0
  3. data/android_tests/lib/android/specs/android/element/text.rb +2 -2
  4. data/android_tests/lib/android/specs/android/helper.rb +2 -2
  5. data/android_tests/lib/android/specs/common/device.rb +10 -28
  6. data/android_tests/lib/android/specs/common/device_touchaction.rb +29 -0
  7. data/android_tests/lib/android/specs/common/helper.rb +4 -4
  8. data/android_tests/lib/android/specs/common/patch.rb +2 -2
  9. data/android_tests/lib/android/specs/driver.rb +11 -1
  10. data/android_tests/lib/android/specs/install.rb +7 -6
  11. data/appium_lib.gemspec +2 -10
  12. data/contributing.md +23 -0
  13. data/docs/android_docs.md +274 -217
  14. data/docs/docs.md +28 -0
  15. data/docs/ios_docs.md +372 -212
  16. data/ios_tests/UICatalog.app/Info.plist +0 -0
  17. data/ios_tests/appium.txt +2 -1
  18. data/ios_tests/lib/ios/specs/common/helper.rb +12 -21
  19. data/ios_tests/lib/ios/specs/common/web_context.rb +7 -2
  20. data/ios_tests/lib/ios/specs/device/device.rb +16 -10
  21. data/ios_tests/lib/ios/specs/driver.rb +13 -2
  22. data/ios_tests/lib/ios/specs/ios/element/button.rb +5 -4
  23. data/ios_tests/lib/ios/specs/ios/element/generic.rb +2 -2
  24. data/ios_tests/lib/ios/specs/ios/element/text.rb +11 -7
  25. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +9 -9
  26. data/lib/appium_lib.rb +1 -17
  27. data/lib/appium_lib/android/helper.rb +55 -5
  28. data/lib/appium_lib/common/helper.rb +26 -30
  29. data/lib/appium_lib/common/patch.rb +12 -0
  30. data/lib/appium_lib/common/version.rb +2 -2
  31. data/lib/appium_lib/device/device.rb +83 -12
  32. data/lib/appium_lib/driver.rb +26 -14
  33. data/lib/appium_lib/ios/element/button.rb +4 -4
  34. data/lib/appium_lib/ios/element/generic.rb +4 -4
  35. data/lib/appium_lib/ios/element/text.rb +4 -4
  36. data/lib/appium_lib/ios/element/textfield.rb +41 -22
  37. data/lib/appium_lib/ios/helper.rb +216 -47
  38. data/lib/appium_lib/ios/patch.rb +0 -19
  39. data/readme.md +1 -0
  40. data/release_notes.md +64 -0
  41. metadata +7 -5
@@ -1,3 +1,5 @@
1
+ require 'selenium/webdriver/common/error'
2
+
1
3
  # Generic helper methods not specific
2
4
  # to a particular tag name
3
5
  module Appium
@@ -53,28 +55,14 @@ module Appium
53
55
  find_elements :xpath, xpath_str
54
56
  end
55
57
 
56
- # Prints xml of the current page
57
- # @return [void]
58
- def source
59
- source = @driver.page_source
58
+ def _print_source source
59
+ opts = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
60
60
  if source.start_with? '<html'
61
- doc = Nokogiri::HTML(source) do |config|
62
- config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
63
- end
64
- puts doc.to_xhtml indent: 2
61
+ doc = Nokogiri::HTML(source) { |cfg| cfg.options = opts }
65
62
  else
66
- doc = Nokogiri::XML(source) do |config|
67
- config.options = Nokogiri::XML::ParseOptions::NOBLANKS | Nokogiri::XML::ParseOptions::NONET
68
- end
69
- puts doc.to_xml indent: 2
63
+ doc = Nokogiri::XML(source) { |cfg| cfg.options = opts }
70
64
  end
71
- end
72
-
73
- # Returns XML string for the current page
74
- # Same as driver.page_source
75
- # @return [String]
76
- def get_source
77
- @driver.page_source
65
+ puts doc.to_xml indent: 2
78
66
  end
79
67
 
80
68
  # @private
@@ -93,7 +81,7 @@ module Appium
93
81
  # http://nokogiri.org/Nokogiri/XML/SAX/Document.html
94
82
  def start_element name, attrs = []
95
83
  # Count only visible elements. Android is always visible
96
- element_visible = $driver.device_is_android? ? true : attrs.to_h['visible'] == 'true'
84
+ element_visible = $driver.device_is_android? ? true : Hash[attrs]['visible'] == 'true'
97
85
  @result[name] += 1 if element_visible
98
86
  end
99
87
 
@@ -140,7 +128,9 @@ module Appium
140
128
 
141
129
  # @private
142
130
  def lazy_load_strings
143
- @strings_xml ||= app_strings
131
+ # app strings only works on local apps.
132
+ # on disk apps (ex: com.android.settings) will error
133
+ @strings_xml ||= ignore { app_strings } || {}
144
134
  end
145
135
 
146
136
  # Search strings.xml's values for target.
@@ -185,16 +175,18 @@ module Appium
185
175
  end
186
176
 
187
177
  def reset
188
- @element_stack = []
178
+ @element_stack = []
189
179
  @elements_in_order = []
190
- @skip_element = false
180
+ @skip_element = false
191
181
  end
192
182
 
193
183
  def result
194
184
  @elements_in_order.reduce('') do |r, e|
195
- name = e.delete :name
185
+ name = e.delete :name
196
186
  attr_string = e.reduce('') do |string, attr|
197
- string += " #{attr[0]}: #{attr[1]}\n"
187
+ attr_1 = attr[1]
188
+ attr_1 = attr_1 ? attr_1.strip : attr_1
189
+ string += " #{attr[0]}: #{attr_1}\n"
198
190
  end
199
191
 
200
192
  unless attr_string.nil? || attr_string.empty?
@@ -207,8 +199,8 @@ module Appium
207
199
  def start_element name, attrs = []
208
200
  @skip_element = filter && !filter.include?(name.downcase)
209
201
  unless @skip_element
210
- element = {name: name}
211
- attrs.each {|a| element[a[0]] = a[1]}
202
+ element = { name: name }
203
+ attrs.each { |a| element[a[0]] = a[1] }
212
204
  @element_stack.push element
213
205
  @elements_in_order.push element
214
206
  end
@@ -216,16 +208,20 @@ module Appium
216
208
 
217
209
  def end_element name
218
210
  return if filter && !filter.include?(name.downcase)
219
- element_index = @element_stack.rindex {|e| e[:name] == name}
211
+ element_index = @element_stack.rindex { |e| e[:name] == name }
220
212
  @element_stack.delete_at element_index
221
213
  end
222
214
 
223
215
  def characters(chars)
224
216
  unless @skip_element
225
- element = @element_stack.last
217
+ element = @element_stack.last
226
218
  element[:text] = chars
227
219
  end
228
220
  end
229
221
  end
222
+
223
+ def _no_such_element
224
+ raise Selenium::WebDriver::Error::NoSuchElementError, 'An element could not be located on the page using the given search parameters.'
225
+ end
230
226
  end # module Common
231
- end # module Appium
227
+ end # module Appium
@@ -1,3 +1,5 @@
1
+ require_relative 'version'
2
+
1
3
  module Appium
2
4
  module Common
3
5
  # Implement useful features for element.
@@ -89,6 +91,7 @@ def patch_webdriver_bridge
89
91
  path_str = path.sub(path_match[0], '') unless path_match.nil?
90
92
 
91
93
  puts "#{verb} #{path_str}"
94
+
92
95
  # must check to see if command_hash is a hash. sometimes it's not.
93
96
  if command_hash.is_a?(Hash) && !command_hash.empty?
94
97
  print_command = command_hash.clone
@@ -104,6 +107,10 @@ def patch_webdriver_bridge
104
107
  else
105
108
  ap print_command
106
109
  end
110
+ else # non-standard command hash
111
+ # It's important to output this for debugging problems.
112
+ # for example invalid JSON will not be a Hash
113
+ ap command_hash if command_hash
107
114
  end
108
115
  delay = $driver.global_webdriver_http_sleep
109
116
  sleep delay if !delay.nil? && delay > 0
@@ -131,4 +138,9 @@ class Selenium::WebDriver::Remote::Response
131
138
 
132
139
  msg
133
140
  end
141
+ end
142
+
143
+ class Selenium::WebDriver::Remote::Http::Common
144
+ remove_const :DEFAULT_HEADERS if defined? DEFAULT_HEADERS
145
+ DEFAULT_HEADERS = { 'Accept' => CONTENT_TYPE, 'User-Agent' => "appium/ruby_lib/#{::Appium::VERSION}" }
134
146
  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 = '4.1.0' unless defined? ::Appium::VERSION
4
- DATE = '2014-07-21' unless defined? ::Appium::DATE
3
+ VERSION = '5.0.0' unless defined? ::Appium::VERSION
4
+ DATE = '2014-12-23' unless defined? ::Appium::DATE
5
5
  end
@@ -8,14 +8,15 @@ module Appium
8
8
  post: {
9
9
  open_notifications: 'session/:session_id/appium/device/open_notifications',
10
10
  shake: 'session/:session_id/appium/device/shake',
11
- launch: 'session/:session_id/appium/app/launch',
11
+ launch_app: 'session/:session_id/appium/app/launch',
12
12
  close_app: 'session/:session_id/appium/app/close',
13
13
  reset: 'session/:session_id/appium/app/reset',
14
14
  toggle_airplane_mode: 'session/:session_id/appium/device/toggle_airplane_mode',
15
15
  },
16
16
  get: {
17
- current_activity: 'session/:session_id/appium/device/current_activity',
18
- current_context: 'session/:session_id/context',
17
+ current_activity: 'session/:session_id/appium/device/current_activity',
18
+ current_context: 'session/:session_id/context',
19
+ get_network_connection: 'session/:session_id/network_connection',
19
20
  }
20
21
  }
21
22
 
@@ -32,7 +33,7 @@ module Appium
32
33
 
33
34
  # @!method current_activity
34
35
 
35
- # @!method launch
36
+ # @!method launch_app
36
37
  # Start the simulator and applicaton configured with desired capabilities
37
38
 
38
39
  # @!method reset
@@ -92,6 +93,13 @@ module Appium
92
93
  # Android only; Ends the test coverage and writes the results to the given path on device.
93
94
  # @param path (String) Path on the device to write too.
94
95
  # @param intent (String) Intent to broadcast when ending coverage.
96
+
97
+ # @!method get_settings
98
+ # Get appium Settings for current test session
99
+
100
+ # @!method update_settings
101
+ # Update appium Settings for current test session
102
+ # @param settings (hash) Settings to update, keys are settings, values to value to set each setting to
95
103
  class << self
96
104
  def extended(mod)
97
105
  extend_webdriver_with_forwardable
@@ -120,15 +128,15 @@ module Appium
120
128
  end
121
129
  end
122
130
 
123
- add_endpoint_method(:install, 'session/:session_id/appium/device/install_app') do
124
- def install(path)
125
- execute :install, {}, :appPath => path
131
+ add_endpoint_method(:install_app, 'session/:session_id/appium/device/install_app') do
132
+ def install_app(path)
133
+ execute :install_app, {}, :appPath => path
126
134
  end
127
135
  end
128
136
 
129
- add_endpoint_method(:remove, 'session/:session_id/appium/device/remove_app') do
130
- def remove(id)
131
- execute :remove, {}, :appId => id
137
+ add_endpoint_method(:remove_app, 'session/:session_id/appium/device/remove_app') do
138
+ def remove_app(id)
139
+ execute :remove_app, {}, :appId => id
132
140
  end
133
141
  end
134
142
 
@@ -144,6 +152,36 @@ module Appium
144
152
  end
145
153
  end
146
154
 
155
+ # @!method start_activity
156
+ # Start a new activity within the current app or launch a new app and start the target activity.
157
+ #
158
+ # Android only.
159
+ # @param [String] The package owning the activity [required]
160
+ # @param [String] The target activity [required]
161
+ # @param [String] The package to start before the target package [optional]
162
+ # @param [String] The activity to start before the target activity [optional]
163
+ #
164
+ # ```ruby
165
+ # start_activity app_package: 'io.appium.android.apis', app_activity: '.accessibility.AccessibilityNodeProviderActivity'
166
+ # ```
167
+ add_endpoint_method(:start_activity, 'session/:session_id/appium/device/start_activity') do
168
+ def start_activity(opts)
169
+ raise 'opts must be a hash' unless opts.is_a? Hash
170
+ app_package = opts[:app_package]
171
+ raise 'app_package is required' unless app_package
172
+ app_activity = opts[:app_activity]
173
+ raise 'app_activity is required' unless opts[:app_activity]
174
+ app_wait_package = opts.fetch(:app_wait_package, '')
175
+ app_wait_activity = opts.fetch(:app_wait_activity, '')
176
+
177
+ unknown_opts = opts.keys - [:app_package, :app_activity, :app_wait_package, :app_wait_activity]
178
+ raise "Unknown options #{unknown_opts}" unless unknown_opts.empty?
179
+
180
+ execute :start_activity, {}, { appPackage: app_package, appActivity: app_activity,
181
+ appWaitPackage: app_wait_package, appWaitActivity: app_wait_activity }
182
+ end
183
+ end
184
+
147
185
  add_endpoint_method(:set_context, 'session/:session_id/context') do
148
186
  def set_context(context=null)
149
187
  execute :set_context, {}, :name => context
@@ -181,7 +219,7 @@ module Appium
181
219
  # TODO TEST ME
182
220
  add_endpoint_method(:set_immediate_value, 'session/:session_id/appium/element/:id/value') do
183
221
  def set_immediate_value(element, value)
184
- execute :set_immediate_value, { :id => element.ref }, value
222
+ execute :set_immediate_value, { :id => element.ref }, value: value
185
223
  end
186
224
  end
187
225
 
@@ -214,6 +252,39 @@ module Appium
214
252
  end
215
253
  end
216
254
 
255
+ add_endpoint_method(:get_settings, 'session/:session_id/appium/settings', :get) do
256
+ def get_settings
257
+ execute :get_settings, {}
258
+ end
259
+ end
260
+
261
+ add_endpoint_method(:update_settings, 'session/:session_id/appium/settings') do
262
+ def update_settings(settings)
263
+ execute :update_settings, {}, settings: settings
264
+ end
265
+ end
266
+
267
+ # @!method get_network_connection
268
+ # Get the device network connection current status
269
+ # See set_network_connection method for return value
270
+
271
+ # @!method set_network_connection
272
+ # Set the device network connection mode
273
+ # @param path (String) Bit mask that represent the network mode
274
+ # Value (Alias) | Data | Wifi | Airplane Mode
275
+ # -------------------------------------------------
276
+ # 1 (Airplane Mode) | 0 | 0 | 1
277
+ # 6 (All network on) | 1 | 1 | 0
278
+ # 4 (Data only) | 1 | 0 | 0
279
+ # 2 (Wifi only) | 0 | 1 | 0
280
+ # 0 (None) | 0 | 0 | 0
281
+ #
282
+ add_endpoint_method(:set_network_connection, 'session/:session_id/network_connection') do
283
+ def set_network_connection(mode)
284
+ execute :set_network_connection, {}, type: mode
285
+ end
286
+ end
287
+
217
288
  add_touch_actions
218
289
  extend_search_contexts
219
290
  end
@@ -280,7 +351,7 @@ module Appium
280
351
  def add_touch_actions
281
352
  add_endpoint_method(:touch_actions, 'session/:session_id/touch/perform') do
282
353
  def touch_actions(actions)
283
- actions = [actions].flatten
354
+ actions = { actions: [actions].flatten }
284
355
  execute :touch_actions, {}, actions
285
356
  end
286
357
  end
@@ -223,19 +223,32 @@ module Appium
223
223
 
224
224
  # attr readers are promoted to global scope. To avoid clobbering, they're
225
225
  # made available via the driver_attributes method
226
+ #
227
+ # attr_accessor is repeated for each one so YARD documents them properly.
228
+
226
229
 
227
230
  # The amount to sleep in seconds before every webdriver http call.
228
- attr_accessor :global_webdriver_http_sleep,
229
- :caps,
230
- :custom_url,
231
- :export_session,
232
- :default_wait,
233
- :last_waits,
234
- :sauce_username,
235
- :sauce_access_key,
236
- :appium_port,
237
- :appium_device,
238
- :appium_debug
231
+ attr_accessor :global_webdriver_http_sleep
232
+ # Selenium webdriver capabilities
233
+ attr_accessor :caps
234
+ # Custom URL for the selenium server
235
+ attr_accessor :custom_url
236
+ # Export session id to textfile in /tmp for 3rd party tools
237
+ attr_accessor :export_session
238
+ # Default wait time for elements to appear
239
+ attr_accessor :default_wait
240
+ # Array of previous wait time values
241
+ attr_accessor :last_waits
242
+ # Username for use on Sauce Labs
243
+ attr_accessor :sauce_username
244
+ # Access Key for use on Sauce Labs
245
+ attr_accessor :sauce_access_key
246
+ # Appium's server port
247
+ attr_accessor :appium_port
248
+ # Device type to request from the appium server
249
+ attr_accessor :appium_device
250
+ # Boolean debug mode for the Appium Ruby bindings
251
+ attr_accessor :appium_debug
239
252
 
240
253
  # Creates a new driver
241
254
  #
@@ -475,8 +488,7 @@ module Appium
475
488
  raise 'ERROR: Unable to connect to Appium. Is the server running?'
476
489
  end
477
490
 
478
- # Set implicit wait by default unless we're using Pry.
479
- @driver.manage.timeouts.implicit_wait = @default_wait unless defined? Pry
491
+ @driver.manage.timeouts.implicit_wait = @default_wait
480
492
 
481
493
  @driver
482
494
  end
@@ -592,4 +604,4 @@ end # module Appium
592
604
  # Paging in Pry is annoying :q required to exit.
593
605
  # With pager disabled, the output is similar to IRB
594
606
  # Only set if Pry is defined.
595
- Pry.config.pager = false if defined?(Pry)
607
+ Pry.config.pager = false if defined?(Pry)
@@ -10,7 +10,7 @@ module Appium
10
10
  def button value
11
11
  # return button at index.
12
12
  return ele_index UIAButton, value if value.is_a? Numeric
13
- xpath_visible_contains UIAButton, value
13
+ ele_by_json_visible_contains UIAButton, value
14
14
  end
15
15
 
16
16
  # Find all UIAButtons containing value.
@@ -19,7 +19,7 @@ module Appium
19
19
  # @return [Array<UIAButton>]
20
20
  def buttons value=false
21
21
  return tags UIAButton unless value
22
- xpaths_visible_contains UIAButton, value
22
+ eles_by_json_visible_contains UIAButton, value
23
23
  end
24
24
 
25
25
  # Find the first UIAButton.
@@ -38,14 +38,14 @@ module Appium
38
38
  # @param value [String] the value to match exactly
39
39
  # @return [UIAButton]
40
40
  def button_exact value
41
- xpath_visible_exact UIAButton, value
41
+ ele_by_json_visible_exact UIAButton, value
42
42
  end
43
43
 
44
44
  # Find all UIAButtons that exactly match value.
45
45
  # @param value [String] the value to match exactly
46
46
  # @return [Array<UIAButton>]
47
47
  def buttons_exact value
48
- xpaths_visible_exact UIAButton, value
48
+ eles_by_json_visible_exact UIAButton, value
49
49
  end
50
50
  end # module Ios
51
51
  end # module Appium
@@ -5,28 +5,28 @@ module Appium
5
5
  # @param value [String] the value to search for
6
6
  # @return [Element]
7
7
  def find value
8
- xpath_visible_contains '*', value
8
+ ele_by_json_visible_contains '*', value
9
9
  end
10
10
 
11
11
  # Find all elements containing value
12
12
  # @param value [String] the value to search for
13
13
  # @return [Array<Element>]
14
14
  def finds value
15
- xpaths_visible_contains '*', value
15
+ eles_by_json_visible_contains '*', value
16
16
  end
17
17
 
18
18
  # Find the first element exactly matching value
19
19
  # @param value [String] the value to search for
20
20
  # @return [Element]
21
21
  def find_exact value
22
- xpath_visible_exact '*', value
22
+ ele_by_json_visible_exact '*', value
23
23
  end
24
24
 
25
25
  # Find all elements exactly matching value
26
26
  # @param value [String] the value to search for
27
27
  # @return [Array<Element>]
28
28
  def finds_exact value
29
- xpaths_visible_exact '*', value
29
+ eles_by_json_visible_exact '*', value
30
30
  end
31
31
  end # module Ios
32
32
  end # module Appium
@@ -9,7 +9,7 @@ module Appium
9
9
  # @return [UIAStaticText]
10
10
  def text value
11
11
  return ele_index UIAStaticText, value if value.is_a? Numeric
12
- xpath_visible_contains UIAStaticText, value
12
+ ele_by_json_visible_contains UIAStaticText, value
13
13
  end
14
14
 
15
15
  # Find all UIAStaticText containing value.
@@ -18,7 +18,7 @@ module Appium
18
18
  # @return [Array<UIAStaticText>]
19
19
  def texts value=false
20
20
  return tags UIAStaticText unless value
21
- xpaths_visible_contains UIAStaticText, value
21
+ eles_by_json_visible_contains UIAStaticText, value
22
22
  end
23
23
 
24
24
  # Find the first UIAStaticText.
@@ -37,14 +37,14 @@ module Appium
37
37
  # @param value [String] the value to match exactly
38
38
  # @return [UIAStaticText]
39
39
  def text_exact value
40
- xpath_visible_exact UIAStaticText, value
40
+ ele_by_json_visible_exact UIAStaticText, value
41
41
  end
42
42
 
43
43
  # Find all UIAStaticTexts that exactly match value.
44
44
  # @param value [String] the value to match exactly
45
45
  # @return [Array<UIAStaticText>]
46
46
  def texts_exact value
47
- xpaths_visible_exact UIAStaticText, value
47
+ eles_by_json_visible_exact UIAStaticText, value
48
48
  end
49
49
  end # module Ios
50
50
  end # module Appium