appium_lib 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +48 -14
  3. data/android_tests/Rakefile +5 -2
  4. data/android_tests/api.apk +0 -0
  5. data/android_tests/lib/android/specs/android/element/alert.rb +1 -1
  6. data/android_tests/lib/android/specs/android/element/button.rb +1 -4
  7. data/android_tests/lib/android/specs/android/element/text.rb +15 -18
  8. data/android_tests/lib/android/specs/android/element/textfield.rb +1 -4
  9. data/android_tests/lib/android/specs/android/helper.rb +4 -6
  10. data/android_tests/lib/android/specs/common/device.rb +16 -6
  11. data/android_tests/lib/android/specs/common/helper.rb +6 -6
  12. data/android_tests/lib/android/specs/common/patch.rb +3 -3
  13. data/android_tests/lib/android/specs/driver.rb +1 -1
  14. data/appium_lib.gemspec +11 -11
  15. data/docs/android_docs.md +193 -210
  16. data/docs/ios_docs.md +505 -177
  17. data/docs/migration.md +27 -0
  18. data/docs_gen/make_docs.rb +17 -17
  19. data/ios_tests/Rakefile +6 -3
  20. data/ios_tests/lib/ios/specs/common/helper.rb +1 -1
  21. data/ios_tests/lib/ios/specs/common/patch.rb +2 -2
  22. data/ios_tests/lib/ios/specs/device/device.rb +3 -2
  23. data/ios_tests/lib/ios/specs/device/multi_touch.rb +1 -1
  24. data/ios_tests/lib/ios/specs/device/touch_actions.rb +2 -2
  25. data/ios_tests/lib/ios/specs/driver.rb +5 -5
  26. data/ios_tests/lib/ios/specs/ios/element/alert.rb +5 -5
  27. data/ios_tests/lib/ios/specs/ios/element/button.rb +2 -5
  28. data/ios_tests/lib/ios/specs/ios/element/text.rb +21 -21
  29. data/ios_tests/lib/ios/specs/ios/element/textfield.rb +1 -8
  30. data/ios_tests/lib/ios/specs/ios/helper.rb +2 -2
  31. data/ios_tests/lib/ios/specs/ios/patch.rb +1 -1
  32. data/ios_tests/lib/run.rb +1 -1
  33. data/ios_tests/upload/sauce_storage.rb +13 -12
  34. data/ios_tests/upload/upload.rb +1 -1
  35. data/lib/appium_lib/android/dynamic.rb +31 -30
  36. data/lib/appium_lib/android/element/button.rb +7 -11
  37. data/lib/appium_lib/android/element/generic.rb +5 -5
  38. data/lib/appium_lib/android/element/text.rb +8 -12
  39. data/lib/appium_lib/android/element/textfield.rb +3 -7
  40. data/lib/appium_lib/android/helper.rb +46 -10
  41. data/lib/appium_lib/common/helper.rb +4 -2
  42. data/lib/appium_lib/common/patch.rb +1 -1
  43. data/lib/appium_lib/common/version.rb +2 -2
  44. data/lib/appium_lib/device/device.rb +34 -27
  45. data/lib/appium_lib/device/multi_touch.rb +1 -1
  46. data/lib/appium_lib/device/touch_actions.rb +30 -28
  47. data/lib/appium_lib/driver.rb +3 -3
  48. data/lib/appium_lib/ios/element/button.rb +4 -8
  49. data/lib/appium_lib/ios/element/text.rb +8 -12
  50. data/lib/appium_lib/ios/element/textfield.rb +3 -7
  51. data/lib/appium_lib/ios/helper.rb +70 -40
  52. data/readme.md +6 -70
  53. data/release_notes.md +26 -0
  54. metadata +3 -2
@@ -74,7 +74,7 @@ module Appium
74
74
  zoom.perform
75
75
  end
76
76
  end
77
-
77
+
78
78
  # Create a new multi-action
79
79
  def initialize
80
80
  @actions = []
@@ -9,20 +9,20 @@ module Appium
9
9
  # action = TouchAction.new.press(x: 45, y: 100).wait(5).release
10
10
  # action.perform
11
11
  class TouchAction
12
- ACTIONS = [:move_to, :press_for_duration, :press, :release, :tap, :wait, :perform]
12
+ ACTIONS = [:move_to, :long_press, :press, :release, :tap, :wait, :perform]
13
13
  COMPLEX_ACTIONS = [:swipe]
14
-
14
+
15
15
  class << self
16
16
  COMPLEX_ACTIONS.each do |action|
17
17
  define_method(action) do |opts|
18
- auto_perform = opts.delete(:auto_perform) {|k| true}
19
- ta = TouchAction.new
18
+ auto_perform = opts.delete(:auto_perform) { |k| true }
19
+ ta = TouchAction.new
20
20
  ta.send(action, opts)
21
21
  return ta unless auto_perform
22
22
  ta.perform
23
23
  end
24
24
  end
25
- end
25
+ end
26
26
 
27
27
  attr_reader :actions
28
28
 
@@ -40,13 +40,14 @@ module Appium
40
40
  end
41
41
 
42
42
  # Press down for a specific duration.
43
- # @param element [WebDriver::Element] the element to press.
44
- # @param x [integer] x co-ordinate to press on.
45
- # @param y [integer] y co-ordinate to press on.
46
- # @param duration [integer] Number of seconds to press.
47
- def press_for_duration(element, x, y, duration)
48
- @actions << {element: element.ref, x: x, y: y, duration: duration}
49
- chain_method(:longPress, args)
43
+ # @option element [WebDriver::Element] the element to press.
44
+ # @option x [integer] x co-ordinate to press on.
45
+ # @option y [integer] y co-ordinate to press on.
46
+ # @option duration [integer] Number of milliseconds to press.
47
+ def long_press(opts)
48
+ args = opts.select { |k, v| [:element, :x, :y, :duration].include? k }
49
+ args = args_with_ele_ref(args)
50
+ chain_method(:longPress, args) # longPress is what the appium server expects
50
51
  end
51
52
 
52
53
  # Press a finger onto the screen. Finger will stay down until you call
@@ -56,7 +57,7 @@ module Appium
56
57
  # @option opts [integer] :x x co-ordinate to press on
57
58
  # @option opts [integer] :y y co-ordinate to press on
58
59
  def press(opts)
59
- args = opts.select {|k, v| [:element, :x, :y].include? k}
60
+ args = opts.select { |k, v| [:element, :x, :y].include? k }
60
61
  args = args_with_ele_ref(args)
61
62
  chain_method(:press, args)
62
63
  end
@@ -78,15 +79,16 @@ module Appium
78
79
  # @option opts [integer] :y y co-ordinate to tap
79
80
  # @option opts [integer] :fingers how many fingers to tap with (Default 1)
80
81
  def tap(opts)
81
- opts[:count] = opts.delete(:fingers) if opts[:fingers]
82
- opts_with_defaults = {count: 1}.merge opts
83
- chain_method(:tap, opts_with_defaults)
82
+ opts[:count] = opts.delete(:fingers) if opts[:fingers]
83
+ opts_with_defaults = { count: 1 }.merge opts
84
+ args = args_with_ele_ref opts
85
+ chain_method(:tap, args)
84
86
  end
85
87
 
86
- # Pause for a number of seconds before the next action
87
- # @param seconds [integer] Number of seconds to pause for
88
- def wait(seconds)
89
- args = {ms: seconds}
88
+ # Pause for a number of milliseconds before the next action
89
+ # @param milliseconds [integer] Number of milliseconds to pause for
90
+ def wait(milliseconds)
91
+ args = { ms: milliseconds }
90
92
  chain_method(:wait, args)
91
93
  end
92
94
 
@@ -95,12 +97,12 @@ module Appium
95
97
  # @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
96
98
  # @option opts [int] :end_x Where to end swiping, on the x axis. Default 0.
97
99
  # @option opts [int] :end_y Where to end swiping, on the y axis. Default 0.
98
- # @option opts [int] :duration How long the actual swipe takes to complete.
100
+ # @option opts [int] :duration How long the actual swipe takes to complete in milliseconds.
99
101
  def swipe(opts)
100
- start_x = opts.fetch :start_x, 0
101
- start_y = opts.fetch :start_y, 0
102
- end_x = opts.fetch :end_x, 0
103
- end_y = opts.fetch :end_y, 0
102
+ start_x = opts.fetch :start_x, 0
103
+ start_y = opts.fetch :start_y, 0
104
+ end_x = opts.fetch :end_x, 0
105
+ end_y = opts.fetch :end_y, 0
104
106
  duration = opts[:duration]
105
107
 
106
108
  self.press x: start_x, y: start_y
@@ -118,7 +120,7 @@ module Appium
118
120
 
119
121
  # Does nothing, currently.
120
122
  def cancel
121
- @actions << {action: cancel}
123
+ @actions << { action: cancel }
122
124
  $driver.touch_actions @actions
123
125
  self
124
126
  end
@@ -127,9 +129,9 @@ module Appium
127
129
 
128
130
  def chain_method(method, args=nil)
129
131
  if args
130
- @actions << {action: method, options: args}
132
+ @actions << { action: method, options: args }
131
133
  else
132
- @actions << {action: method}
134
+ @actions << { action: method }
133
135
  end
134
136
  self
135
137
  end
@@ -105,7 +105,7 @@ module Appium
105
105
  # ensure files are absolute
106
106
  r.map! do |file|
107
107
  file = File.exists?(file) ? file :
108
- File.join(parent_dir, file)
108
+ File.join(parent_dir, file)
109
109
  file = File.expand_path file
110
110
 
111
111
  File.exists?(file) ? file : nil
@@ -162,7 +162,7 @@ module Appium
162
162
  end
163
163
  # override unless there's an existing method with matching arity
164
164
  end unless const.respond_to?(m) &&
165
- const.method(m).arity == $driver.method(m).arity
165
+ const.method(m).arity == $driver.method(m).arity
166
166
  end
167
167
  end
168
168
  end
@@ -296,7 +296,7 @@ module Appium
296
296
  @@loaded = true
297
297
  # load device methods exactly once
298
298
  extend Appium::Device
299
-
299
+
300
300
  # Promote only on Minitest::Spec (minitest 5) by default
301
301
  Appium.promote_appium_methods ::Minitest::Spec
302
302
  end
@@ -13,10 +13,12 @@ module Appium
13
13
  xpath_visible_contains UIAButton, value
14
14
  end
15
15
 
16
- # Find all UIAButtons containing value
16
+ # Find all UIAButtons containing value.
17
+ # If value is omitted, all UIAButtons are returned.
17
18
  # @param value [String] the value to search for
18
19
  # @return [Array<UIAButton>]
19
- def buttons value
20
+ def buttons value=false
21
+ return tags UIAButton unless value
20
22
  xpaths_visible_contains UIAButton, value
21
23
  end
22
24
 
@@ -45,11 +47,5 @@ module Appium
45
47
  def buttons_exact value
46
48
  xpaths_visible_exact UIAButton, value
47
49
  end
48
-
49
- # Find all UIAButtons.
50
- # @return [Array<UIAButton>]
51
- def e_buttons
52
- tags UIAButton
53
- end
54
50
  end # module Ios
55
51
  end # module Appium
@@ -7,48 +7,44 @@ module Appium
7
7
  # @param value [String, Integer] the value to find.
8
8
  # If int then the UIAStaticText at that index is returned.
9
9
  # @return [UIAStaticText]
10
- def s_text value
10
+ def text value
11
11
  return ele_index UIAStaticText, value if value.is_a? Numeric
12
12
  xpath_visible_contains UIAStaticText, value
13
13
  end
14
14
 
15
15
  # Find all UIAStaticText containing value.
16
+ # If value is omitted, all UIAStaticTexts are returned
16
17
  # @param value [String] the value to search for
17
18
  # @return [Array<UIAStaticText>]
18
- def s_texts value
19
+ def texts value=false
20
+ return tags UIAStaticText unless value
19
21
  xpaths_visible_contains UIAStaticText, value
20
22
  end
21
23
 
22
24
  # Find the first UIAStaticText.
23
25
  # @return [UIAStaticText]
24
- def first_s_text
26
+ def first_text
25
27
  first_ele UIAStaticText
26
28
  end
27
29
 
28
30
  # Find the last UIAStaticText.
29
31
  # @return [UIAStaticText]
30
- def last_s_text
32
+ def last_text
31
33
  last_ele UIAStaticText
32
34
  end
33
35
 
34
36
  # Find the first UIAStaticText that exactly matches value.
35
37
  # @param value [String] the value to match exactly
36
38
  # @return [UIAStaticText]
37
- def s_text_exact value
39
+ def text_exact value
38
40
  xpath_visible_exact UIAStaticText, value
39
41
  end
40
42
 
41
43
  # Find all UIAStaticTexts that exactly match value.
42
44
  # @param value [String] the value to match exactly
43
45
  # @return [Array<UIAStaticText>]
44
- def s_texts_exact value
46
+ def texts_exact value
45
47
  xpaths_visible_exact UIAStaticText, value
46
48
  end
47
-
48
- # Find all UIAStaticTexts.
49
- # @return [Array<UIAStaticText>]
50
- def e_s_texts
51
- tags UIAStaticText
52
- end
53
49
  end # module Ios
54
50
  end # module Appium
@@ -49,9 +49,11 @@ module Appium
49
49
  end
50
50
 
51
51
  # Find all TextFields containing value.
52
+ # If value is omitted, all TextFields are returned.
52
53
  # @param value [String] the value to search for
53
54
  # @return [Array<TextField>]
54
- def textfields value
55
+ def textfields value=false
56
+ return xpaths _textfield_visible_string unless value
55
57
  xpaths _textfield_contains_string value
56
58
  end
57
59
 
@@ -80,11 +82,5 @@ module Appium
80
82
  def textfields_exact value
81
83
  xpaths _textfield_exact_string value
82
84
  end
83
-
84
- # Find all TextFields.
85
- # @return [Array<TextField>]
86
- def e_textfields
87
- xpaths _textfield_visible_string
88
- end
89
85
  end # module Ios
90
86
  end # module Appium
@@ -1,4 +1,4 @@
1
- module Appium
1
+ module Appium
2
2
  module Ios
3
3
  # iOS only. On Android uiautomator always returns an empty string for EditText password.
4
4
  #
@@ -6,7 +6,7 @@
6
6
  # @param length [Integer] the length of the password to generate
7
7
  # @return [String] the returned string is of size length
8
8
  def ios_password length=1
9
- '' * length
9
+ 8226.chr('UTF-8') * length
10
10
  end
11
11
 
12
12
  # Returns a string of interesting elements. iOS only.
@@ -14,10 +14,12 @@
14
14
  # Defaults to inspecting the 1st windows source only.
15
15
  # use get_page(get_source) for all window sources
16
16
  #
17
- # @param element [Object] the element to search. omit to search everything
17
+ # @option element [Object] the element to search. omit to search everything
18
+ # @option class_name [String,Symbol] the class name to filter on. case insensitive include match.
18
19
  # @return [String]
19
20
  def get_page element=source_window(0), class_name=nil
20
21
  lazy_load_strings # populate @strings_xml
22
+ class_name = class_name.to_s.downcase
21
23
 
22
24
  # @private
23
25
  def empty ele
@@ -50,43 +52,54 @@
50
52
  type = fix_space element['type']
51
53
 
52
54
  # if class_name is set, mark non-matches as invisible
53
- visible = (type == class_name).to_s if class_name
54
-
55
- if name == label && name == value
56
- puts "#{type}" if name || label || value || hint
57
- puts " name, label, value: #{name}" if name
58
- puts " hint: #{hint}" if hint
59
- elsif name == label
60
- puts "#{type}" if name || label || value || hint
61
- puts " name, label: #{name}" if name
62
- puts " value: #{value}" if value
63
- puts " hint: #{hint}" if hint
64
- elsif name == value
65
- puts "#{type}" if name || label || value || hint
66
- puts " name, value: #{name}" if name
67
- puts " label: #{label}" if label
68
- puts " hint: #{hint}" if hint
69
- else
70
- puts "#{type}" if name || label || value || hint
71
- puts " name: #{name}" if name
72
- puts " label: #{label}" if label
73
- puts " value: #{value}" if value
74
- puts " hint: #{hint}" if hint
75
- end if visible && visible == 'true'
76
-
77
- # there may be many ids with the same value.
78
- # output all exact matches.
79
- id_matches = @strings_xml.select do |key, val|
80
- val == name || val == label || val == value
81
- end
55
+ visible = (type.downcase.include?(class_name)).to_s if class_name
56
+ if visible && visible == 'true'
57
+ if name == label && name == value
58
+ puts "#{type}" if name || label || value || hint
59
+ puts " name, label, value: #{name}" if name
60
+ puts " hint: #{hint}" if hint
61
+ elsif name == label
62
+ puts "#{type}" if name || label || value || hint
63
+ puts " name, label: #{name}" if name
64
+ puts " value: #{value}" if value
65
+ puts " hint: #{hint}" if hint
66
+ elsif name == value
67
+ puts "#{type}" if name || label || value || hint
68
+ puts " name, value: #{name}" if name
69
+ puts " label: #{label}" if label
70
+ puts " hint: #{hint}" if hint
71
+ else
72
+ puts "#{type}" if name || label || value || hint
73
+ puts " name: #{name}" if name
74
+ puts " label: #{label}" if label
75
+ puts " value: #{value}" if value
76
+ puts " hint: #{hint}" if hint
77
+ end
78
+
79
+ # there may be many ids with the same value.
80
+ # output all exact matches.
81
+ attributes = [name, label, value, hint].select { |attr| !attr.nil? }
82
+ partial = {}
83
+ id_matches = @strings_xml.select do |key, val|
84
+ next if val.nil? || val.empty?
85
+ partial[key] = val if attributes.detect { |attr| attr.include?(val) }
86
+ attributes.detect { |attr| val == attr }
87
+ end
88
+
89
+ # If there are no exact matches, display partial matches.
90
+ id_matches = partial if id_matches.empty?
91
+
92
+ unless id_matches.empty?
93
+ match_str = ''
94
+ max_len = id_matches.keys.max_by(&:length).length
82
95
 
83
- if id_matches && id_matches.length > 0
84
- match_str = ''
85
- # [0] = key, [1] = value
86
- id_matches.each do |match|
87
- match_str += ' ' * 7 + "#{match[0]}\n"
96
+ # [0] = key, [1] = value
97
+ id_matches.each do |key, value|
98
+ arrow_space = ' ' * (max_len - key.length).to_i
99
+ match_str += ' ' * 7 + "#{key} #{arrow_space}=> #{value}\n"
100
+ end
101
+ puts " id: #{match_str.strip}\n"
88
102
  end
89
- puts " id: #{match_str.strip}\n"
90
103
  end
91
104
  end
92
105
 
@@ -96,10 +109,27 @@
96
109
  end
97
110
 
98
111
  # Prints a string of interesting elements to the console.
112
+ #
113
+ # Example
114
+ #
115
+ # ```ruby
116
+ # page class: :UIAButton # filter on buttons
117
+ # page window: 1 # show source for window 1
118
+ # page class: :UIAButton, window: 1
119
+ # ```
120
+ #
121
+ # @option window [Integer] window index. -1 for default
122
+ # @option class [Symbol] class name to filter on
123
+ #
99
124
  # @return [void]
100
125
  def page opts={}
101
- window_number = opts.fetch :window, -1
102
- class_name = opts.fetch :class, nil
126
+ if opts.is_a?(Hash)
127
+ window_number = opts.fetch :window, -1
128
+ class_name = opts.fetch :class, nil
129
+ else
130
+ window_number = -1
131
+ class_name = opts
132
+ end
103
133
 
104
134
  if window_number == -1
105
135
  # if the 0th window has no children, find the next window that does.
data/readme.md CHANGED
@@ -6,11 +6,11 @@
6
6
 
7
7
  Helper methods for writing cross platform (iOS, Android) tests in Ruby using Appium. Note that user waits should not exceed 120 seconds if they're going to run on Sauce Labs.
8
8
 
9
- Make sure you're using Ruby 1.9.3+ with upgraded rubygems and bundler.
9
+ Make sure you're using Appium 1.0.0 or newer and Ruby 1.9.3+ with upgraded rubygems and bundler.
10
10
 
11
11
  #### Start appium server
12
12
 
13
- `node server.js`
13
+ `node .`
14
14
 
15
15
  #### Install / Upgrade
16
16
 
@@ -28,84 +28,20 @@ gem uninstall -aIx appium_lib ;\
28
28
  gem install --no-rdoc --no-ri appium_lib
29
29
  ```
30
30
 
31
- #### Simple Usage
32
-
33
- ```ruby
34
- require 'rubygems'
35
- require 'appium_lib'
36
-
37
- # Start iOS driver
38
-
39
- # use appium's specific capability names
40
- appium_capabilities = { launchTimeout: 123 }
41
- # there are also built in capabilities such as device that don't require 'raw'
42
- caps = { device: :ios, app_path: '/Users/user/woven/ruby_lib_ios/UICatalog.app', raw: appium_capabilities }
43
- Appium::Driver.new(caps).start_driver
44
-
45
- # Start Android driver
46
- apk = {
47
- device: :android,
48
- app_path: '/path/to/the.apk',
49
- app_package: 'com.example.pkg',
50
- app_activity: '.act.Start',
51
- app_wait_activity: '.act.Start'
52
- }
53
- Appium::Driver.new(apk).start_driver
54
-
55
- # Define the methods on all objects.
56
- # Note that this can also be scoped to limit the potential for conflicts.
57
- # Example: Appium.promote_appium_methods ::Minitest::Spec
58
- # Another alternative is to not promote at all. Instead access methods via $driver
59
- Appium.promote_appium_methods Object
60
- ```
61
-
62
- ```ruby
63
- # Example of automating Settings preinstalled app on Android
64
- # Find these values using arc then type current_app
65
- apk = {
66
- device: :android,
67
- app_path: '',
68
- app_package: 'com.android.settings',
69
- app_activity: '.Settings',
70
- app_wait_activity: '.Settings'
71
- }
72
- Appium::Driver.new(apk).start_driver
73
- ```
74
-
75
- #### Other capabilities
76
-
77
- - `no_reset` If true, the app will not be reset
78
- - `full_reset` If true, the app will be uninstalled. When false, fast reset is activated.
79
-
80
- #### iOS env vars
81
-
82
- - `APP_PATH` Path to the .app folder
83
-
84
- #### Android env vars
85
-
86
- - `APP_PATH` Path to the apk
87
- - `APP_PACKAGE` The APK's package
88
- - `APP_ACTIVITY` Activity to start
89
- - `APP_WAIT_ACTIVITY` Optional. Activity to wait for.
90
-
91
31
  #### Sauce Labs env vars
92
32
 
93
- - `APP_NAME` Name of the test run
94
33
  - `SAUCE_USERNAME` Sauce username
95
34
  - `SAUCE_ACCESS_KEY` Sauce API key
96
35
 
97
36
  #### Troubleshooting
98
37
 
99
38
  1. Does `adb kill-server; adb devices` list an active Android device?
100
- 2. Have you defined the proper env vars? `APP_PATH, APP_PACKAGE, APP_ACTIVITY, APP_WAIT_ACTIVITY`
101
- 3. Are you running appium from source? `node server.js`
39
+ 3. Are you running appium from source? `node .`
102
40
 
103
41
  #### Documentation
104
42
 
105
43
  - [Installing Appium on OS X](https://github.com/appium/ruby_console/blob/master/osx.md)
106
44
  - [Overview](https://github.com/appium/ruby_lib/blob/master/docs/docs.md)
107
- - [Android methods](https://github.com/appium/ruby_lib/blob/master/docs/android_docs.md)
108
- - [iOS methods](https://github.com/appium/ruby_lib/blob/master/docs/ios_docs.md)
109
- - [Appium docs](https://github.com/appium/appium/tree/master/docs)
110
- - [Ruby iOS tests](https://github.com/appium/ruby_lib_ios)
111
- - [Ruby Android tests](https://github.com/appium/ruby_lib_android)
45
+ - [Ruby Android methods](https://github.com/appium/ruby_lib/blob/master/docs/android_docs.md)
46
+ - [Ruby iOS methods](https://github.com/appium/ruby_lib/blob/master/docs/ios_docs.md)
47
+ - [Appium Server docs](https://github.com/appium/appium/tree/master/docs)