appium_lib 0.0.30 → 0.3.0

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