appium_lib 0.3.16 → 0.4.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.
@@ -0,0 +1,44 @@
1
+ Automatically convert yard's HTML output to Markdown. Run in Chrome Dev Tools console.
2
+
3
+ ```javascript
4
+ // http://localhost:8808/docs/toplevel
5
+
6
+ var methods = $x('//*[@id="content"]/ul/li');
7
+
8
+ var sig = function(method) { return method.children[0].children[0].textContent.trim().substr(2); };
9
+ var desc = function(method) { return method.children[1].children[0].textContent.trim(); };
10
+
11
+ // All element methods must include the element name.
12
+ // Check textfield before text because text is included in textfield.
13
+ var elements = [ 'alert', 'button', 'textfield', 'text', 'window' ];
14
+ var output = {};
15
+
16
+ for (var b = 0; b < elements.length; b++) {
17
+ output[elements[b]] = [];
18
+ }
19
+
20
+ for (var a = 0; a < methods.length; a++) {
21
+ var name = sig(methods[a]);
22
+ var text = desc(methods[a]);
23
+ var index = '';
24
+
25
+ for (var b = 0; b < elements.length; b++) {
26
+ if (name.indexOf(elements[b]) !== -1 && name.indexOf('find_ele') === -1) {
27
+ index = elements[b];
28
+ break;
29
+ }
30
+ }
31
+
32
+ if (index === '') { continue; }
33
+ output[index].push( [name, text] );
34
+ }
35
+
36
+ for (var b = 0; b < elements.length; b++) {
37
+ var eles = output[elements[b]];
38
+ console.log("\n" + '#### ' + elements[b]);
39
+ for (var c = 0; c < eles.length; c++) {
40
+ var ele = eles[c];
41
+ console.log('0. `' + ele[0] + '` ' + ele[1]);
42
+ }
43
+ }
44
+ ```
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'yard'
5
+
6
+ def space amount
7
+ '&nbsp;' * amount
8
+ end
9
+
10
+ def last_sha
11
+ `git rev-parse --verify HEAD`.strip
12
+ end
13
+
14
+ def mobj_to_md obj
15
+ out = ''
16
+ # skip objects without method signatures
17
+ sig = obj.signature
18
+ return out unless sig
19
+
20
+ # skip class vars
21
+ if sig.start_with?('@@') ||
22
+ # skip methods marked private
23
+ obj.tag('private') ||
24
+ # skip date and version from version.rb
25
+ obj.name.match(/DATE|VERSION/)
26
+ return out
27
+ end
28
+
29
+ method_path = obj.file.split('/lib/').last
30
+ os = method_path.downcase.match /ios|android/
31
+ out += "##### [#{obj.name.to_s}](https://github.com/appium/ruby_lib/blob/#{last_sha}/lib/#{method_path}#L#{obj.line}) #{os}\n\n"
32
+ out += "> #{obj.signature}\n\n"
33
+ out += "#{obj.docstring}\n\n"
34
+
35
+
36
+ indent = space 5
37
+ params = obj.tags.select { |tag| tag.tag_name == 'param' }
38
+ if !params.empty?
39
+ out += "__Parameters:__\n\n"
40
+ params.each do |param|
41
+ out += indent + "[#{param.types.join ', '}] "
42
+ out += "#{param.name} - #{param.text}\n\n"
43
+ end
44
+ end
45
+
46
+ ret = obj.tag 'return'
47
+ if ret
48
+ out += "__Returns:__\n\n"
49
+ out += indent + "[#{ret.types.join ', '}] #{ret.text}\n\n"
50
+ end
51
+ out += "--\n\n"
52
+
53
+ out
54
+ end
55
+
56
+ def run out_file, globs
57
+ YARD::Registry.clear
58
+ puts "Globbing: #{globs}"
59
+ puts "Writing: #{out_file}"
60
+ YARD::Parser::SourceParser.parse globs
61
+ File.open(out_file, 'w') do | file |
62
+ YARD::Registry.entries.each do | entry |
63
+ file.write mobj_to_md entry
64
+ end
65
+ end
66
+ end
67
+
68
+ def globs paths
69
+ # Convert single string to array for map
70
+ paths = [ paths ] unless paths.kind_of? Array
71
+ # Adjust path based on system
72
+ paths.map! { |path| "#{ENV['HOME']}/Desktop/appium/ruby_lib#{path}" }
73
+ end
74
+
75
+ common_globs = '/lib/appium_lib/*.rb', '/lib/appium_lib/common/**/*.rb'
76
+ android_globs = common_globs + [ '/lib/appium_lib/android/**/*.rb' ]
77
+ ios_globs = common_globs + [ '/lib/appium_lib/ios/**/*.rb' ]
78
+
79
+ run 'docs/android_docs.md', globs(android_globs)
80
+
81
+ run 'docs/ios_docs.md', globs(ios_globs)
@@ -2,6 +2,7 @@
2
2
 
3
3
  $driver = nil
4
4
 
5
+ # @private
5
6
  # Invoke top level methods on last created Appium driver.
6
7
  def self.method_missing method, *args, &block
7
8
  raise "driver is nil. called #{method}" if $driver == nil
@@ -12,6 +13,7 @@ def self.method_missing method, *args, &block
12
13
  end
13
14
 
14
15
  module Appium
16
+ # @private
15
17
  def self.add_to_path file, path=false
16
18
  path = path ? "../#{path}/" : '..'
17
19
  path = File.expand_path path, file
@@ -22,4 +24,4 @@ module Appium
22
24
  add_to_path __FILE__
23
25
 
24
26
  require 'appium_lib/driver'
25
- end
27
+ end
@@ -34,4 +34,4 @@ module Appium::Android
34
34
  def alert_dismiss_text
35
35
  first_button.text
36
36
  end
37
- end # module Appium::Android
37
+ end # module Appium::Android
@@ -8,22 +8,22 @@ In Android //* is used to find partial case insensitive text matches.
8
8
 
9
9
  find_element :name by default uses a partial case insensitive match.
10
10
  On iOS the default is an exact name match.
11
- =end
12
11
 
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);
12
+ ```javascript
13
+ // iOS version
14
+ // https://github.com/appium/ruby_lib/blob/37bb4e90b29e5adb4438b287b6387a504c94b5c4/lib/appium_lib/element/ios/generic.rb#L23
15
+ var search = "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'";
16
+ var a = w.secureTextFields().firstWithPredicate(search);
17
+ if ( isNil(a) ) {
18
+ a = w.textFields().firstWithPredicate(search);
19
+ if ( isNil(a) ) {
20
+ a = w.buttons().firstWithPredicate(search);
18
21
  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
- }
22
+ a = w.elements().firstWithPredicate(search);
26
23
  }
24
+ }
25
+ }
26
+ ```
27
27
 
28
28
  Android considers both a textfield and a secure textfield to be "EditText".
29
29
  Name (the content desc) is searched first and then we search for value (text).
@@ -35,6 +35,12 @@ so we consider the element a button if the class name contains the word button.
35
35
  After looking for textfields and buttons, then we search all elements. Find will return
36
36
  the first element that matches.
37
37
  =end
38
+
39
+ # Find the value contained in content description or text. Search elements
40
+ # in this order: EditText, Button, ImageButton
41
+ #
42
+ # @param val [String] the value to search for
43
+ # @return [Element]
38
44
  def find val
39
45
  # s.className('android.widget.EditText').descriptionContains(value);
40
46
  args = [ [4, 'android.widget.EditText'], [7, val] ],
@@ -1,9 +1,12 @@
1
1
  # encoding: utf-8
2
2
  module Appium::Android
3
3
  # Returns an array of android classes that match the tag name
4
+ # @param tag_name [String] the tag name to convert to an android class
5
+ # @return [String]
4
6
  def tag_name_to_android tag_name
5
7
  tag_name = tag_name.to_s.downcase.strip
6
8
 
9
+ # @private
7
10
  def prefix *tags
8
11
  tags.map!{ |tag| "android.widget.#{tag}" }
9
12
  end
@@ -33,8 +36,11 @@ module Appium::Android
33
36
  raise "Invalid tag name #{tag_name}"
34
37
  end # return result of case
35
38
  end
36
-
37
- # on android, assume the attr is name (which falls back to text).
39
+ # Find all elements matching the attribute
40
+ # On android, assume the attr is name (which falls back to text).
41
+ #
42
+ # @param tag_name [String] the tag name to search for
43
+ # @return [Element]
38
44
  def find_eles_attr tag_name
39
45
  =begin
40
46
  sel1 = [ [4, 'android.widget.Button'], [100] ]
@@ -54,7 +60,11 @@ module Appium::Android
54
60
  mobile :find, array
55
61
  end
56
62
 
63
+ # Selendroid only.
64
+ # Returns a string containing interesting elements.
65
+ # @return [String]
57
66
  def get_selendroid_inspect
67
+ # @private
58
68
  def run node
59
69
  r = []
60
70
 
@@ -116,7 +126,10 @@ module Appium::Android
116
126
  end
117
127
 
118
128
  # Android only.
129
+ # Returns a string containing interesting elements.
130
+ # @return [String]
119
131
  def get_android_inspect
132
+ # @private
120
133
  def run node
121
134
  r = []
122
135
 
@@ -161,11 +174,14 @@ module Appium::Android
161
174
  out
162
175
  end
163
176
 
177
+ # Automatically detects selendroid or android.
178
+ # Returns a string containing interesting elements.
179
+ # @return [String]
164
180
  def get_inspect
165
181
  @selendroid ? get_selendroid_inspect : get_android_inspect
166
182
  end
167
183
 
168
- # Android only. Intended for use with console.
184
+ # Intended for use with console.
169
185
  # Inspects and prints the current page.
170
186
  def page
171
187
  puts get_inspect
@@ -174,8 +190,10 @@ module Appium::Android
174
190
 
175
191
  # JavaScript code from https://github.com/appium/appium/blob/master/app/android.js
176
192
  #
193
+ # ```javascript
177
194
  # Math.round((duration * 1000) / 200)
178
195
  # (.20 * 1000) / 200 = 1
196
+ # ```
179
197
  #
180
198
  # We want steps to be exactly 1. If it's zero then a tap is used instead of a swipe.
181
199
  def fast_duration
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  module Appium::Android
3
+ # @private
3
4
  # class_eval inside a method because class Selenium::WebDriver::Element
4
5
  # will trigger as soon as the file is required. in contrast a method
5
6
  # will trigger only when invoked.
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module Appium
4
+ class Driver
5
+ @@map = {
6
+ 1 => [ 'text(String text)', 'SELECTOR_TEXT', 1],
7
+ 2 => [ 'textStartsWith(String text)', 'SELECTOR_START_TEXT', 2],
8
+ 3 => [ 'textContains(String text)', 'SELECTOR_CONTAINS_TEXT', 3],
9
+ 4 => [ 'className(String className)', 'SELECTOR_CLASS', 4],
10
+ 5 => [ 'description(String desc)', 'SELECTOR_DESCRIPTION', 5],
11
+ 6 => [ 'descriptionStartsWith(String desc)', 'SELECTOR_START_DESCRIPTION', 6],
12
+ 7 => [ 'descriptionContains(String desc)', 'SELECTOR_CONTAINS_DESCRIPTION', 7],
13
+ 8 => [ 'index(final int index)', 'SELECTOR_INDEX', 8],
14
+ 9 => [ 'instance(final int instance)', 'SELECTOR_INSTANCE', 9],
15
+ 10 => [ 'enabled(boolean val)', 'SELECTOR_ENABLED', 10],
16
+ 11 => [ 'focused(boolean val)', 'SELECTOR_FOCUSED', 11],
17
+ 12 => [ 'focusable(boolean val)', 'SELECTOR_FOCUSABLE', 12],
18
+ 13 => [ 'scrollable(boolean val)', 'SELECTOR_SCROLLABLE', 13],
19
+ 14 => [ 'clickable(boolean val)', 'SELECTOR_CLICKABLE', 14],
20
+ 15 => [ 'checked(boolean val)', 'SELECTOR_CHECKED', 15],
21
+ 16 => [ 'selected(boolean val)', 'SELECTOR_SELECTED', 16],
22
+ # // SELECTOR_ID = 17; // nothing.
23
+ 18 => [ 'packageName(String name)', 'SELECTOR_PACKAGE_NAME', 18],
24
+ # // SELECTOR_CHILD = 19; // childSelector(UiSelector selector)
25
+ # // SELECTOR_CONTAINER = 20; // containerSelector(UiSelector selector)
26
+ # // SELECTOR_PATTERN = 21; // ! private ! patternSelector(UiSelector selector)
27
+ # // SELECTOR_PARENT = 22; // fromParent(UiSelector selector)
28
+ # // SELECTOR_COUNT = 23; // nothing.
29
+ 24 => [ 'longClickable(boolean val)', 'SELECTOR_LONG_CLICKABLE', 24],
30
+ 25 => [ 'textMatches(String regex)', 'SELECTOR_TEXT_REGEX', 25],
31
+ 26 => [ 'classNameMatches(String regex)', 'SELECTOR_CLASS_REGEX', 26],
32
+ 27 => [ 'descriptionMatches(String regex)', 'SELECTOR_DESCRIPTION_REGEX', 27],
33
+ 28 => [ 'packageNameMatches(String regex)', 'SELECTOR_PACKAGE_NAME_REGEX', 28],
34
+ # // start internal methods at 100
35
+ 100 => [ 'getStringAttribute("name")', 'GET_NAME', 100]
36
+ }
37
+
38
+ def dynamic_code_to_string code, value=false
39
+ result = @@map[code].first
40
+ return result unless value
41
+ result.split('(').first + "( \"#{value}\" )"
42
+ end
43
+ end # class Driver
44
+ end # module Appium
@@ -58,33 +58,35 @@ module Appium::Common
58
58
  end
59
59
 
60
60
  # Find by id. Useful for selendroid
61
+ # @param id [String] the id to search for
62
+ # @return [Element]
61
63
  def id id
62
64
  find_element :id, id
63
65
  end
64
66
 
65
- # Presses the back button on Android.
67
+ # Navigate back.
66
68
  # @return [void]
67
69
  def back
68
70
  @driver.navigate.back
69
71
  end
70
72
 
71
- # For Sauce Labs reporting
73
+ # For Sauce Labs reporting. Returns the current session id.
72
74
  def session_id
73
75
  @driver.session_id
74
76
  end
75
77
 
76
- def sauce_username
77
- @sauce_username
78
- end
79
-
80
- def sauce_access_key
81
- @sauce_access_key
82
- end
83
-
78
+ # Returns the first element that matches the provided xpath.
79
+ #
80
+ # @param xpath_str [String] the XPath string
81
+ # @return [Element]
84
82
  def xpath xpath_str
85
83
  find_element :xpath, xpath_str
86
84
  end
87
85
 
86
+ # Returns all elements that match the provided xpath.
87
+ #
88
+ # @param xpath_str [String] the XPath string
89
+ # @return [Array<Element>]
88
90
  def xpaths xpath_str
89
91
  find_elements :xpath, xpath_str
90
92
  end
@@ -185,11 +187,19 @@ module Appium::Common
185
187
  JSON.parse @driver.page_source, max_nesting: 9999
186
188
  end
187
189
 
190
+ # Returns the first element that matches name
191
+ #
192
+ # @param name [String] the name to match
193
+ # @return [Element]
188
194
  def find_name name
189
195
  find_element :name, name
190
196
  end
191
197
 
198
+ # Returns all elements that match name
199
+ #
200
+ # @param name [String] the name to match
201
+ # @return [Array<Element>]
192
202
  def find_names name
193
203
  find_elements :name, name
194
204
  end
195
- end # module Appium::Common
205
+ end # module Appium::Common
@@ -4,19 +4,21 @@ module Appium::Common
4
4
  class Selenium::WebDriver::Element
5
5
  # Note: For testing .text should be used over value, and name.
6
6
 
7
- # Fixes NoMethodError: undefined method `value' for #<Selenium::WebDriver::Element:0x..fa4a9148235390a44 id="1">
7
+ # Returns the value attribute
8
+ #
9
+ # Fixes NoMethodError: undefined method `value' for Selenium::WebDriver::Element
8
10
  def value
9
11
  self.attribute :value
10
12
  end
11
13
 
12
- # Fixes NoMethodError: undefined method `name' for #<Selenium::WebDriver::Element
14
+ # Returns the name attribute
15
+ #
16
+ # Fixes NoMethodError: undefined method `name' for Selenium::WebDriver::Element
13
17
  def name
14
18
  self.attribute :name
15
19
  end
16
20
 
17
- # Use tag_name to get element's type.
18
- #
19
- # Tag name appears to be the same as type.
21
+ # Returns the type attribute
20
22
  #
21
23
  # Fixes Selenium::WebDriver::Error::UnknownError: Not yet implemented
22
24
  def tag_name
@@ -25,7 +27,9 @@ module Appium::Common
25
27
 
26
28
  # For use with mobile tap.
27
29
  #
30
+ # ```ruby
28
31
  # execute_script 'mobile: tap', :x => 0.0, :y => 0.98
32
+ # ```
29
33
  #
30
34
  # https://github.com/appium/appium/wiki/Automating-mobile-gestures
31
35
  # @return [OpenStruct] the relative x, y in a struct. ex: { x: 0.50, y: 0.20 }
@@ -49,6 +53,7 @@ require 'selenium/webdriver/remote/commands'
49
53
  require 'selenium/webdriver/remote/http/common'
50
54
  require 'selenium/webdriver/remote/http/default'
51
55
 
56
+ # @private
52
57
  # Show http calls to the Selenium server.
53
58
  #
54
59
  # Invaluable for debugging.
@@ -81,7 +86,40 @@ def patch_webdriver_bridge
81
86
  unless command_hash.nil? || command_hash.length == 0
82
87
  print_command = command_hash.clone
83
88
  print_command.delete :args if print_command[:args] == []
84
- ap print_command
89
+
90
+ mobile_find = 'mobile: find'
91
+ if print_command[:script] == mobile_find
92
+ args = print_command[:args]
93
+ puts "#{mobile_find}"#" #{args}"
94
+
95
+ # [[[[3, "sign"]]]] => [[[3, "sign"]]]
96
+ #
97
+ # [[[[4, "android.widget.EditText"], [7, "z"]], [[4, "android.widget.EditText"], [3, "z"]]]]
98
+ # => [[[4, "android.widget.EditText"], [7, "z"]], [[4, "android.widget.EditText"], [3, "z"]]]
99
+ args = args[0]
100
+ option = args[0].to_s.downcase
101
+ has_option = ! option.match(/all|scroll/).nil?
102
+ puts option if has_option
103
+
104
+ start = has_option ? 1 : 0
105
+
106
+ start.upto(args.length-1) do |selector_index|
107
+ selectors = args[selector_index]
108
+ selectors_size = selectors.length
109
+ selectors.each_index do |pair_index|
110
+ pair = selectors[pair_index]
111
+ res = $driver.dynamic_code_to_string pair[0], pair[1]
112
+
113
+ if selectors_size == 1 || pair_index >= selectors_size - 1
114
+ puts res
115
+ elsif selectors_size > 1 && pair_index < selectors_size
116
+ print res + '.'
117
+ end
118
+ end
119
+ end # start.upto
120
+ else
121
+ ap print_command
122
+ end
85
123
  end
86
124
  # puts "verb: #{verb}, path #{path}, command_hash #{command_hash.to_json}"
87
125
  http.call verb, path, command_hash
@@ -91,6 +129,7 @@ end # def
91
129
 
92
130
  # Print Appium's origValue error messages.
93
131
  class Selenium::WebDriver::Remote::Response
132
+ # @private
94
133
  def error_message
95
134
  val = value
96
135