appium_lib 0.0.30 → 0.3.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 (38) hide show
  1. checksums.yaml +8 -8
  2. data/Rakefile +15 -7
  3. data/appium_lib.gemspec +3 -3
  4. data/docs.md +7 -5
  5. data/lib/appium_lib.rb +34 -6
  6. data/lib/appium_lib/android/element/alert.rb +43 -0
  7. data/lib/appium_lib/android/element/generic.rb +94 -0
  8. data/lib/appium_lib/android/element/textfield.rb +43 -0
  9. data/lib/appium_lib/android/helper.rb +120 -0
  10. data/lib/appium_lib/android/patch.rb +10 -0
  11. data/lib/appium_lib/common/element/button.rb +83 -0
  12. data/lib/appium_lib/common/element/text.rb +44 -0
  13. data/lib/appium_lib/common/element/window.rb +9 -0
  14. data/lib/appium_lib/common/helper.rb +140 -0
  15. data/lib/appium_lib/common/patch.rb +83 -0
  16. data/lib/appium_lib/common/version.rb +6 -0
  17. data/lib/appium_lib/driver.rb +265 -0
  18. data/lib/appium_lib/ios/element/alert.rb +56 -0
  19. data/lib/appium_lib/ios/element/generic.rb +170 -0
  20. data/lib/appium_lib/ios/element/textfield.rb +90 -0
  21. data/lib/appium_lib/ios/helper.rb +103 -0
  22. data/lib/appium_lib/ios/patch.rb +15 -0
  23. data/readme.md +10 -3
  24. data/release_notes.md +8 -0
  25. metadata +19 -15
  26. data/lib/appium_lib/console.rb +0 -254
  27. data/lib/appium_lib/element/android/alert.rb +0 -45
  28. data/lib/appium_lib/element/android/generic.rb +0 -88
  29. data/lib/appium_lib/element/android/textfield.rb +0 -44
  30. data/lib/appium_lib/element/button.rb +0 -83
  31. data/lib/appium_lib/element/ios/alert.rb +0 -49
  32. data/lib/appium_lib/element/ios/generic.rb +0 -140
  33. data/lib/appium_lib/element/ios/textfield.rb +0 -93
  34. data/lib/appium_lib/element/text.rb +0 -43
  35. data/lib/appium_lib/element/window.rb +0 -12
  36. data/lib/appium_lib/helper.rb +0 -278
  37. data/lib/appium_lib/patch.rb +0 -90
  38. data/lib/appium_lib/version.rb +0 -4
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZmFjYzZhZTk4ZDNmNDJhMTliNmYyYjM0MTcyYzRhZjlkZWI2YzhhNg==
4
+ YmE2MWU3M2FmMDI5OWVkNzQ3Mzk5ZjAzMjdkODcxNGFiNmIzNzI1Yw==
5
5
  data.tar.gz: !binary |-
6
- YmEzZmMyOWJiMThkMWM2NzJiMjYyMWRjNWNlZDI0NTg3MDFkZGEyMA==
6
+ MDdiZGM4MjAzZjU3OTQ4MTAxZjJiNjg2MTdmNzEzNjFlN2U5ZjMxMw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- N2E0OTFjYjIxZTg1ODkzNTFlNGI3ZGE2NmI2YjZkMjQ4MTVjMjc3ZGFkMDRi
10
- ODM1NmNiNTZiOWJkODNmZTQ2ZmUxZDI2NzcyMGFlY2ZiNjRlNDlhM2IxMzIw
11
- YTA0YjY3N2JmNmNiMWM5Y2RmYTE0OGEwYmFmMmQwYzQzYmU1YWQ=
9
+ NDk5OWIyNjgzYzg3M2NiNmE2OGJiZmI3MDM5YTM5NDg4MmNiYTE0Nzg2YTlj
10
+ MGEyZTA0NTdjZjYyYWQxMGE2ZjY4MDkyOWE1NzRkYTFjZTQ0MWU3NTcwMGI1
11
+ NzAyZmQ4ODhkYzBkMmFiM2RkYzQ0MjlhNDA3NGYxNWJlOWNiNzI=
12
12
  data.tar.gz: !binary |-
13
- NDQ4OTQwNjQyN2I3MTA5NjRkYjM2MWJkMGZlNjY0MTA4MTdiMzllZmM0ZWY1
14
- ZTE0OWQ5OTVkYzRiZTQ5NDExMDQ4YTRmYjU0ZTQ3ZTRhYmEwMDY3NzlhMTM3
15
- NGI2ZDNhODhjMDRhOWRlMjMyNDllMDYxYWRkOWU0YWU4MGI1YjI=
13
+ NjU2M2Q2NWE0MWY4NTg5NTAwNzFjYzcyYjg0NjNhYzU2YTNlZDI4MTFjY2Nm
14
+ YTIzMTFmZGZhMTA4YTc4MDIyZmZkMGY0ZDczZWIyMjdiZTk4ZjM5MTRlOWI1
15
+ NThkMjVjZjU2MTAxODhhYTQyNmM4MTZjNDFlOTU1M2IwYjhlYjg=
data/Rakefile CHANGED
@@ -1,12 +1,13 @@
1
+ # encoding: utf-8
1
2
  require 'rubygems'
2
3
  require 'rake'
3
4
  require 'date'
4
5
 
5
6
  # Defines gem name.
6
- def repo_name; 'appium_lib'; end # ruby_lib published as appium_lib
7
- def gh_name; 'ruby_lib'; end # the name as used on github.com
8
- def version_file; "lib/#{repo_name}/version.rb"; end
9
- def version_rgx; /VERSION = '([^']+)'/m; end
7
+ def repo_name; 'appium_lib' end # ruby_lib published as appium_lib
8
+ def gh_name; 'ruby_lib' end # the name as used on github.com
9
+ def version_file; "lib/#{repo_name}/common/version.rb" end
10
+ def version_rgx; /VERSION = '([^']+)'/m end
10
11
 
11
12
  def version
12
13
  @version = @version || File.read(version_file).match(version_rgx)[1]
@@ -79,9 +80,16 @@ desc 'Build a new gem (same as gem task)'
79
80
  task :build => :gem do
80
81
  end
81
82
 
83
+ desc 'Uninstall gem'
84
+ task :uninstall do
85
+ cmd = "gem uninstall -aIx #{repo_name}"
86
+ puts cmd
87
+ # rescue on gem not installed error.
88
+ begin; `cmd`; rescue; end
89
+ end
90
+
82
91
  desc 'Install gem'
83
- task :install => :gem do
84
- `gem uninstall -aIx #{repo_name}`
92
+ task :install => [ :gem, :uninstall ] do
85
93
  sh "gem install --no-rdoc --no-ri #{repo_name}-#{version}.gem"
86
94
  end
87
95
 
@@ -112,7 +120,7 @@ task :notes do
112
120
  data =`git log --pretty=oneline #{pairs[a]}`
113
121
  new_data = ''
114
122
  data.split("\n").each do |line|
115
- hex = line.match(/[a-zA-Z0-9]+/)[0];
123
+ hex = line.match(/[a-zA-Z0-9]+/)[0]
116
124
  # use first 7 chars to match GitHub
117
125
  new_data += "- [#{hex[0...7]}](https://github.com/appium/#{gh_name}/commit/#{hex}) #{line.gsub(hex, '').strip}\n"
118
126
  end
data/appium_lib.gemspec CHANGED
@@ -8,15 +8,15 @@ end
8
8
 
9
9
  add_to_path 'lib'
10
10
 
11
- require 'appium_lib/version'
11
+ require 'appium_lib/common/version'
12
12
 
13
13
  Gem::Specification.new do |s|
14
14
  # 1.8.x is not supported
15
15
  s.required_ruby_version = '>= 1.9.3'
16
16
 
17
17
  s.name = 'appium_lib'
18
- s.version = AppiumLib::VERSION
19
- s.date = AppiumLib::DATE
18
+ s.version = Appium::VERSION
19
+ s.date = Appium::DATE
20
20
  s.license = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
21
21
  s.description = s.summary = 'Ruby lib for use with Appium'
22
22
  s.description += '.' # avoid identical warning
data/docs.md CHANGED
@@ -14,7 +14,9 @@
14
14
  - [mechanic names of elements](https://github.com/jaykz52/mechanic/blob/8c490e1d225f384847e47ffdafb47cc2248bb96c/src/mechanic-core.js#L28)
15
15
  - [All methods supported by Appium](https://github.com/appium/appium/wiki/JSON-Wire-Protocol:-Supported-Methods)
16
16
  - [Appium's mobile gesture docs](https://github.com/appium/appium/wiki/Automating-mobile-gestures)
17
- -
17
+
18
+ --
19
+
18
20
  Example use of Appium's mobile gesture.
19
21
 
20
22
  > @driver.execute_script 'mobile: tap', :x => 0, :y => 500
@@ -31,7 +33,6 @@ textfield | UIATextField
31
33
  secure | UIASecureTextField
32
34
  text | UIAStaticText
33
35
 
34
-
35
36
  --
36
37
 
37
38
  #### generic
@@ -45,6 +46,7 @@ iOS uses accessibility label with a fallback to text.
45
46
  - `(Array<Element>) names(name)` Returns all elements containing name.
46
47
  - `(Element) text(text)` Returns the first element containing text.
47
48
  - `(Array<Element>) texts(text)` Returns all elements containing text.
49
+
48
50
  --
49
51
 
50
52
  #### alert
@@ -152,7 +154,7 @@ s.value == password('hello'.length)
152
154
  `execute_script 'UIATarget.localTarget().frontMostApp().buttons()[0].tap()'`
153
155
 
154
156
  See [app.js](https://github.com/appium/appium/blob/master/app/uiauto/appium/app.js#L3) for more au methods.
155
- Note that raw UIAutomation commands are not offically supported.
157
+ Note that raw UIAutomation commands are not officially supported.
156
158
 
157
159
  Advanced au.
158
160
 
@@ -214,7 +216,7 @@ at_exit do
214
216
  # $driver.session_id
215
217
  ID = $driver.send(:bridge).session_id
216
218
  driver_quit
217
-
219
+
218
220
  if !SAUCE_USERNAME.nil? && !SAUCE_ACCESS_KEY.nil?
219
221
  URL = "https://#{SAUCE_USERNAME}:#{SAUCE_ACCESS_KEY}@saucelabs.com/rest/v1/#{SAUCE_USERNAME}/jobs/#{ID}"
220
222
 
@@ -228,4 +230,4 @@ at_exit do
228
230
  end
229
231
  end
230
232
  end
231
- ```
233
+ ```
data/lib/appium_lib.rb CHANGED
@@ -1,10 +1,38 @@
1
- def self.add_to_path file, path=false
2
- path = path ? "../#{path}/" : '..'
3
- path = File.expand_path path, file
1
+ # encoding: utf-8
4
2
 
5
- $:.unshift path unless $:.include? path
3
+ $last_driver = nil
4
+
5
+ # Invoke top level methods on last created Appium driver.
6
+ def self.method_missing method, *args, &block
7
+ raise "driver is nil. called #{method}" if $last_driver == nil
8
+
9
+ has_args = ! args.empty?
10
+ has_block = ! block.nil?
11
+
12
+ # method with no params
13
+ if ! has_args && ! has_block
14
+ $last_driver.send method
15
+ # method with only arg params
16
+ elsif has_args && ! has_block
17
+ $last_driver.send method, *args
18
+ # method with only block param
19
+ elsif ! has_args && has_block
20
+ $last_driver.send method, block
21
+ # method with arg + block
22
+ elsif has_args && has_block
23
+ $last_driver.send method, *args, block
24
+ end
6
25
  end
7
26
 
8
- add_to_path __FILE__, 'appium_lib'
27
+ module Appium
28
+ def self.add_to_path file, path=false
29
+ path = path ? "../#{path}/" : '..'
30
+ path = File.expand_path path, file
9
31
 
10
- require 'console'
32
+ $:.unshift path unless $:.include? path
33
+ end
34
+
35
+ add_to_path __FILE__
36
+
37
+ require 'appium_lib/driver'
38
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ module Appium::Android
3
+ # Tap the alert button identified by value.
4
+ # @param value [Integer, String] either an integer index of the button or the button's name
5
+ # @return [void]
6
+ def alert_click value
7
+ button(value).click
8
+ end
9
+
10
+ # Get the alert message text.
11
+ # @return [String]
12
+ def alert_text
13
+ get_page
14
+ end
15
+
16
+ # Accept the alert.
17
+ # The last button is considered "accept."
18
+ # @return [void]
19
+ def alert_accept
20
+ last_button.click
21
+ end
22
+
23
+ # Get the text of the alert's accept button.
24
+ # The last button is considered "accept."
25
+ # @return [String]
26
+ def alert_accept_text
27
+ last_button.text
28
+ end
29
+
30
+ # Dismiss the alert.
31
+ # The first button is considered "dismiss."
32
+ # @return [void]
33
+ def alert_dismiss
34
+ first_button.click
35
+ end
36
+
37
+ # Get the text of the alert's dismiss button.
38
+ # The first button is considered "dismiss."
39
+ # @return [String]
40
+ def alert_dismiss_text
41
+ first_button.text
42
+ end
43
+ end # module Appium::Android
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+ module Appium::Android
3
+ =begin
4
+ name, names, text, text should match substring and case insensitive.
5
+
6
+ In Android //* is used to find partial case insensitive text matches.
7
+ //* is not currently implemented in iOS.
8
+
9
+ find_element :name by default uses a partial case insensitive match.
10
+ On iOS the default is an exact name match.
11
+ =end
12
+
13
+ =begin
14
+ // iOS version
15
+ // https://github.com/appium/ruby_lib/blob/37bb4e90b29e5adb4438b287b6387a504c94b5c4/lib/appium_lib/element/ios/generic.rb#L23
16
+ var search = "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'";
17
+ var a = w.secureTextFields().firstWithPredicate(search);
18
+ if ( isNil(a) ) {
19
+ a = w.textFields().firstWithPredicate(search);
20
+ if ( isNil(a) ) {
21
+ a = w.buttons().firstWithPredicate(search);
22
+ if ( isNil(a) ) {
23
+ a = w.elements().firstWithPredicate(search);
24
+ }
25
+ }
26
+ }
27
+
28
+ Android considers both a textfield and a secure textfield to be "EditText".
29
+ Name (the content desc) is searched first and then we search for value (text).
30
+ There's no label in Android.
31
+
32
+ Android buttons have different class names (android.widget.Button, android.widget.ImageButton)
33
+ so we consider the element a button if the class name contains the word button.
34
+
35
+ After looking for textfields and buttons, then we search all elements. Find will return
36
+ the first element that matches.
37
+ =end
38
+ def find val
39
+ # s.className('android.widget.EditText').descriptionContains(value);
40
+ args = [ [4, 'android.widget.EditText'], [7, val] ],
41
+ # s.className('android.widget.EditText').textContains(value);
42
+ [ [4, 'android.widget.EditText'], [3, val] ],
43
+
44
+ # s.className('android.widget.Button').descriptionContains(value);
45
+ [ [4, 'android.widget.Button'], [7, val] ],
46
+ # s.className('android.widget.Button').textContains(value);
47
+ [ [4, 'android.widget.Button'], [3, val] ],
48
+
49
+ # s.className('android.widget.ImageButton').descriptionContains(value);
50
+ [ [4, 'android.widget.ImageButton'], [7, val] ],
51
+ # s.className('android.widget.ImageButton').textContains(value);
52
+ [ [4, 'android.widget.ImageButton'], [3, val] ],
53
+
54
+ # s.descriptionContains(value);
55
+ [ [7, val] ],
56
+ # s.textContains(value);
57
+ [ [3, val] ]
58
+ mobile :find, args
59
+ end
60
+
61
+ # Return the first element matching text.
62
+ # @param text [String] the text to search for
63
+ # @return [Element] the first matching element
64
+ def text text
65
+ # Return the first element matching selector.
66
+ # s.textContains(value)
67
+ mobile :find, [ [ [3, text] ] ]
68
+ end
69
+
70
+ # Return all elements matching text.
71
+ # @param text [String] the text to search for
72
+ # @return [Array<Element>] all matching elements
73
+ def texts text
74
+ @driver.find_elements :xpath, "//*[contains(@text, '#{text}')]"
75
+ end
76
+
77
+ # Return the first element matching name.
78
+ # on Android name is content description
79
+ # on iOS name is the accessibility label or the text.
80
+ # @param name [String] the name to search for
81
+ # @return [Element] the first matching element
82
+ def name name
83
+ @driver.find_element :name, name
84
+ end
85
+
86
+ # Return all elements matching name.
87
+ # on Android name is content description
88
+ # on iOS name is the accessibility label or the text.
89
+ # @param name [String] the name to search for
90
+ # @return [Array<Element>] all matching elements
91
+ def names name
92
+ @driver.find_elements :name, name
93
+ end
94
+ end # module Appium::Android
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ module Appium::Android
3
+ # UIATextField methods
4
+
5
+ # Get an array of textfield texts.
6
+ # @return [Array<String>]
7
+ def textfields
8
+ find_eles_attr :textfield, :text
9
+ end
10
+
11
+ # Get an array of textfield elements.
12
+ # @return [Array<Textfield>]
13
+ def e_textfields
14
+ find_eles :textfield
15
+ end
16
+
17
+ # Get the first textfield element.
18
+ # @return [Textfield]
19
+ def first_textfield
20
+ first_ele :textfield
21
+ end
22
+
23
+ # Get the last textfield element.
24
+ # @return [Textfield]
25
+ def last_textfield
26
+ last_ele :textfield
27
+ end
28
+
29
+ # Get the first textfield that matches text.
30
+ # @param text [String, Integer] the text to match exactly. If int then the textfield at that index is returned.
31
+ # @return [Textfield]
32
+ def textfield text
33
+ return ele_index :textfield, text if text.is_a? Numeric
34
+ find_ele_by_text :textfield, text
35
+ end
36
+
37
+ # Get the first textfield that includes text.
38
+ # @param text [String] the text the textfield must include
39
+ # @return [Textfield]
40
+ def textfield_include text
41
+ find_ele_by_text_include :textfield, text
42
+ end
43
+ end # module Appium::Android
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+ module Appium::Android
3
+ # Returns an array of android classes that match the tag name
4
+ def tag_name_to_android tag_name
5
+ tag_name = tag_name.to_s.downcase.strip
6
+
7
+ def prefix *tags
8
+ tags.map!{ |tag| "android.widget.#{tag}" }
9
+ end
10
+ # note that 'secure' is not an allowed tag name on android
11
+ # because android can't tell what a secure textfield is
12
+ # they're all edittexts.
13
+
14
+ # must match names in AndroidElementClassMap (Appium's Java server)
15
+ case tag_name
16
+ when 'button'
17
+ prefix 'Button', 'ImageButton'
18
+ when 'text'
19
+ prefix 'TextView'
20
+ when 'list'
21
+ prefix 'ListView'
22
+ when 'window', 'frame'
23
+ prefix 'FrameLayout'
24
+ when 'grid'
25
+ prefix 'GridView'
26
+ when 'relative'
27
+ prefix 'RelativeLayout'
28
+ when 'linear'
29
+ prefix 'LinearLayout'
30
+ when 'textfield'
31
+ prefix 'EditText'
32
+ else
33
+ raise "Invalid tag name #{tag_name}"
34
+ end # return result of case
35
+ end
36
+
37
+ # on android, assume the attr is name (which falls back to text).
38
+ def find_eles_attr tag_name
39
+ =begin
40
+ sel1 = [ [4, 'android.widget.Button'], [100] ]
41
+ sel2 = [ [4, 'android.widget.ImageButton'], [100] ]
42
+
43
+ args = [ 'all', sel1, sel2 ]
44
+
45
+ mobile :find, args
46
+ =end
47
+ array = ['all']
48
+
49
+ tag_name_to_android(tag_name).each do |name|
50
+ # sel.className(name).getStringAttribute("name")
51
+ array.push [ [4, name], [100] ]
52
+ end
53
+
54
+ mobile :find, array
55
+ end
56
+
57
+
58
+ # Android only.
59
+ def get_inspect
60
+ def run node
61
+ r = []
62
+
63
+ run_internal = lambda do |node|
64
+ if node.kind_of? Array
65
+ node.each { |node| run_internal.call node }
66
+ return
67
+ end
68
+
69
+ keys = node.keys
70
+ return if keys.empty?
71
+
72
+ obj = {}
73
+ obj.merge!( { desc: node['@content-desc'] } ) if keys.include?('@content-desc') && !node['@content-desc'].empty?
74
+ obj.merge!( { text: node['@text'] } ) if keys.include?('@text') && !node['@text'].empty?
75
+ obj.merge!( { class: node['@class'] } ) if keys.include?('@class') && !obj.empty?
76
+
77
+ r.push obj if !obj.empty?
78
+ run_internal.call node['node'] if keys.include?('node')
79
+ end
80
+
81
+ run_internal.call node
82
+ r
83
+ end
84
+
85
+ json = JSON.parse(@driver.page_source)
86
+ node = json['hierarchy']
87
+ results = run node
88
+
89
+ out = ''
90
+ results.each { |e|
91
+ out += e[:class].split('.').last + "\n"
92
+
93
+ out += " class: #{e[:class]}\n"
94
+ if e[:text] == e[:desc]
95
+ out += " text, name: #{e[:text]}\n" unless e[:text].nil?
96
+ else
97
+ out += " text: #{e[:text]}\n" unless e[:text].nil?
98
+ out += " name: #{e[:desc]}\n" unless e[:desc].nil?
99
+ end
100
+ }
101
+ out
102
+ end
103
+
104
+ # Android only. Intended for use with console.
105
+ # Inspects and prints the current page.
106
+ def page
107
+ puts get_inspect
108
+ nil
109
+ end
110
+
111
+ # JavaScript code from https://github.com/appium/appium/blob/master/app/android.js
112
+ #
113
+ # Math.round((duration * 1000) / 200)
114
+ # (.20 * 1000) / 200 = 1
115
+ #
116
+ # We want steps to be exactly 1. If it's zero then a tap is used instead of a swipe.
117
+ def fast_duration
118
+ 0.20
119
+ end
120
+ end # module Appium::Android