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
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ module Appium::Ios
3
+ # iOS only
4
+ # Tap the alert button identified by value.
5
+ #
6
+ # Click the ok button:
7
+ # alert_click 'OK'
8
+ #
9
+ # Click the first button:
10
+ # alert_click 0
11
+ #
12
+ # @param value [Integer, String] either an integer index of the button or the button's name
13
+ # @return [void]
14
+ def alert_click value
15
+ value = "'#{value}'" if value.is_a?(String)
16
+ @driver.execute_script "UIATarget.localTarget().frontMostApp().alert().buttons()[#{value}].tap();"
17
+ end
18
+
19
+ # Get the alert message text.
20
+ # @return [String]
21
+ def alert_text
22
+ @driver.switch_to.alert.text
23
+ end
24
+
25
+ # Accept the alert.
26
+ # @return [void]
27
+ def alert_accept
28
+ @driver.switch_to.alert.accept
29
+ end
30
+
31
+ # Get the text of the alert's accept button.
32
+ # The last button is considered "accept."
33
+ # @return [String]
34
+ def alert_accept_text
35
+ a = @driver.find_element(:tag_name, :alert)
36
+ return if a.nil?
37
+ b = a.find_elements(:tag_name, :button)
38
+ b.last.text if b && b.size >= 1
39
+ end
40
+
41
+ # Dismiss the alert.
42
+ # @return [void]
43
+ def alert_dismiss
44
+ @driver.switch_to.alert.dismiss
45
+ end
46
+
47
+ # Get the text of the alert's dismiss button.
48
+ # The first button is considered "dismiss."
49
+ # @return [String]
50
+ def alert_dismiss_text
51
+ a = @driver.find_element(:tag_name, :alert)
52
+ return if a.nil?
53
+ b = a.find_elements(:tag_name, :button)
54
+ b.first.text if b && b.size >= 1
55
+ end
56
+ end # module Appium::Ios
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ module Appium::Ios
3
+ =begin
4
+ name, names, text, text should match substring and case insensitive.
5
+
6
+ iOS .name() is the accessibility attribute. If not defined, then .label() is used instead.
7
+ This differs from Android where name (the content description) is empty when not set.
8
+
9
+ name defaults to label when undefined. value is never a default so that must be
10
+ included in a new search.
11
+
12
+ Find - search everything.
13
+
14
+ The search order is:
15
+ 1. name
16
+ 2. label (implied by name)
17
+ 3. value
18
+
19
+ Android name = iOS name & label
20
+ Android text = iOS value
21
+ =end
22
+
23
+ # returnElems requires a wrapped $(element).
24
+ # set to empty array when length is zero to prevent hang.
25
+ #
26
+ # UIAElementNil when not matched
27
+ #
28
+ # 1. secureTextFields
29
+ # 2. textFields
30
+ # 3. buttons
31
+ # 4. elements
32
+ #
33
+ # search takes the window to search.
34
+ # start searching webview first.
35
+ # window 0 is the main window.
36
+ # window 1 is the 1st webview (if it exists)
37
+ # must break instead of return because it's not a function.
38
+ #
39
+ # single element length is undefined when found and 0 when not found.
40
+ def first_ele_js predicate
41
+ %Q(
42
+ function isNil( a ) {
43
+ return a.type() === 'UIAElementNil';
44
+ }
45
+
46
+ function search( w ) {
47
+ var search = "#{predicate}";
48
+ var a = w.secureTextFields().firstWithPredicate(search);
49
+ if ( isNil(a) ) {
50
+ a = w.textFields().firstWithPredicate(search);
51
+ if ( isNil(a) ) {
52
+ a = w.buttons().firstWithPredicate(search);
53
+ if ( isNil(a) ) {
54
+ a = w.elements().firstWithPredicate(search);
55
+ }
56
+ }
57
+ }
58
+
59
+ return a;
60
+ }
61
+
62
+ function search_web( windowIndex ) {
63
+ var a = undefined;
64
+
65
+ try {
66
+ a = UIATarget.localTarget().frontMostApp().windows()[windowIndex].scrollViews()[0].webViews()[0].elements().firstWithPredicate("#{predicate}");
67
+ } catch(e) {}
68
+
69
+ return a;
70
+ }
71
+
72
+ function run() {
73
+ var windows = au.mainApp.windows();
74
+ for (var i = 0, len = windows.length; i < len; i++) {
75
+ var result = search_web( i );
76
+ if ( isNil( result ) ) {
77
+ result = search( windows[ i ] );
78
+ }
79
+ if ( ! isNil( result ) ) {
80
+ return au._returnElems( $( [ result ] ) );
81
+ }
82
+ }
83
+ return au._returnElems( $( [] ) );
84
+ }
85
+
86
+ run();
87
+ )
88
+ end
89
+
90
+ def all_ele_js predicate
91
+ %Q(
92
+ var w = au.mainWindow;
93
+ var search = "#{predicate}";
94
+ var a = w.elements().withPredicate(search).toArray();
95
+
96
+ if ( a.length === 0 ) {
97
+ a = [];
98
+ }
99
+
100
+ au._returnElems($(a));
101
+ )
102
+ end
103
+
104
+ # Return the first element matching text.
105
+ # @param text [String] the text to search for
106
+ # @return [Element] the first matching element
107
+ def find text
108
+ js = first_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'"
109
+
110
+ ele = execute_script(js).first
111
+ raise Selenium::WebDriver::Error::NoSuchElementError, '' if ele.nil?
112
+ ele
113
+ end
114
+
115
+ # Return all elements matching text.
116
+ # @param text [String] the text to search for
117
+ # @return [Array<Element>] all matching elements
118
+ def finds text
119
+ # returnElems requires a wrapped $(element).
120
+ # must call toArray when using withPredicate instead of firstWithPredicate.
121
+ js = all_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'"
122
+
123
+ execute_script js
124
+ end
125
+
126
+ # Return the first element matching text.
127
+ # @param text [String] the text to search for
128
+ # @return [Element] the first matching element
129
+ def text text
130
+ js = first_ele_js "value contains[c] '#{text}'"
131
+
132
+ execute_script(js).first
133
+ end
134
+
135
+ # Return all elements matching text.
136
+ # @param text [String] the text to search for
137
+ # @return [Array<Element>] all matching elements
138
+ def texts text
139
+ # XPath //* is not implemented on iOS
140
+ # https://github.com/appium/appium/issues/430
141
+ js = all_ele_js "value contains[c] '#{text}'"
142
+
143
+ execute_script js
144
+ end
145
+
146
+ # Return the first element matching name.
147
+ # on Android name is content description
148
+ # on iOS name is the accessibility label or the text.
149
+ # @param name [String] the name to search for
150
+ # @return [Element] the first matching element
151
+ def name name
152
+ js = first_ele_js "name contains[c] '#{name}' || label contains[c] '#{name}'"
153
+
154
+ execute_script(js).first
155
+ end
156
+
157
+ # Return all elements matching name.
158
+ # on Android name is content description
159
+ # on iOS name is the accessibility label or the text.
160
+ # @param name [String] the name to search for
161
+ # @return [Array<Element>] all matching elements
162
+ def names name
163
+ # find_elements :name is not the same as on Android.
164
+ # it's case sensitive and exact on iOS and not on Android.
165
+ # https://github.com/appium/appium/issues/379
166
+ js = all_ele_js "name contains[c] '#{name}' || label contains[c] '#{name}''"
167
+
168
+ execute_script js
169
+ end
170
+ end # module Appium::Ios
@@ -0,0 +1,90 @@
1
+ # encoding: utf-8
2
+ module Appium::Ios
3
+ # UIATextField & UIASecureTextField methods
4
+ #
5
+ # Find textfield and then secure elements in one server call
6
+ # to match Android.
7
+
8
+ # Get an array of textfield texts.
9
+ # @return [Array<String>]
10
+ def textfields
11
+ find_2_eles_attr :textfield, :secure, :text
12
+ end
13
+
14
+ # Get an array of textfield elements.
15
+ # @return [Array<Textfield>]
16
+ def e_textfields
17
+ execute_script textfield_js
18
+ end
19
+
20
+ # Get the first textfield element.
21
+ # @return [Textfield]
22
+ def first_textfield
23
+ js = textfield_js 'r = r.length > 0 ? $(r[0]) : r;'
24
+ execute_script(js).first
25
+ end
26
+
27
+ # Get the last textfield element.
28
+ # @return [Textfield]
29
+ def last_textfield
30
+ js = textfield_js 'r = r.length > 0 ? $(r[r.length - 1]) : r;'
31
+ execute_script(js).first
32
+ end
33
+
34
+ # Get the first textfield that matches text.
35
+ # @param text [String, Integer] the text to match exactly. If int then the textfield at that index is returned.
36
+ # @return [Textfield]
37
+ def textfield text
38
+ # Don't use ele_index because that only works on one element type.
39
+ # iOS needs to combine textfield and secure to match Android.
40
+ if text.is_a? Numeric
41
+ js = textfield_js "r = r.length > 0 ? $(r[#{text}]) : r;"
42
+ return execute_script(js).first
43
+ end
44
+
45
+ textfield_include text
46
+ end
47
+
48
+ # Get the first textfield that includes text.
49
+ # @param text [String] the text the textfield must include
50
+ # @return [Textfield]
51
+ def textfield_include text
52
+ js = %Q(
53
+ var t = au.getElementsByXpath('textfield[contains(@text, "#{text}")]').value;
54
+ var s = au.getElementsByXpath('secure[contains(@text, "#{text}")]').value;
55
+ t.concat(s)[0];
56
+ )
57
+
58
+ puts js if defined? Pry
59
+
60
+ execute_script js
61
+ end
62
+
63
+ # Get the first textfield that exactly matches text.
64
+ # @param text [String] the text the textfield must exactly match
65
+ # @return [Textfield]
66
+ def textfield_exact text
67
+ # find_ele_by_text :textfield, text
68
+ js = %Q(
69
+ var t = au.getElementsByXpath('textfield[@text="#{text}"]').value;
70
+ var s = au.getElementsByXpath('secure[@text="#{text}"]').value;
71
+ t.concat(s)[0];
72
+ )
73
+
74
+ puts js if defined? Pry
75
+
76
+ execute_script js
77
+ end
78
+
79
+ # Return combined lookup of textfield and secure
80
+ # with an optional filter. $() wrap is required for .each
81
+ def textfield_js filter=''
82
+ %Q(
83
+ var t = au.lookup('textfield');
84
+ var s = au.lookup('secure');
85
+ var r = $(t.concat(s));
86
+ #{filter}
87
+ au._returnElems(r);
88
+ )
89
+ end
90
+ end # module Appium::Ios
@@ -0,0 +1,103 @@
1
+ # encoding: utf-8
2
+ module Appium::Ios
3
+ # iOS only. Android uses uiautomator instead of uiautomation.
4
+ # Get an array of attribute values from elements exactly matching tag name.
5
+ # @param tag_name [String] the tag name to find
6
+ # @param attribute [String] the attribute to collect
7
+ # @result [Array<String>] an array of strings containing the attribute from found elements of type tag_name.
8
+ def find_eles_attr tag_name, attribute
9
+ # Use au.lookup(tag_name) instead of $(tag_name)
10
+ # See https://github.com/appium/appium/issues/214
11
+ js = %Q(
12
+ var eles = au.lookup('#{tag_name}');
13
+ var result = [];
14
+ for (var a = 0, length = eles.length; a < length; a++) {
15
+ result.push(eles[a].#{attribute}());
16
+ }
17
+ result
18
+ )
19
+
20
+ @driver.execute_script js
21
+ end
22
+
23
+ # iOS only. Android doesn't use find_2_eles_attr.
24
+ # Get an array of attribute values from elements exactly matching tag name.
25
+ # @param tag_name_1 [String] the 1st tag name to find
26
+ # @param tag_name_2 [String] the 2nd tag name to find
27
+ # @param attribute [String] the attribute to collect
28
+ # @result [Array<String>] an array of strings containing the attribute from found elements of type tag_name.
29
+ def find_2_eles_attr tag_name_1, tag_name_2, attribute
30
+ # Use au.lookup(tag_name) instead of $(tag_name)
31
+ # See https://github.com/appium/appium/issues/214
32
+ js = %Q(
33
+ var eles = au.lookup('#{tag_name_1}');
34
+ eles = $(eles.concat(au.lookup('#{tag_name_2}')));
35
+ var result = [];
36
+ for (var a = 0, length = eles.length; a < length; a++) {
37
+ result.push(eles[a].#{attribute}());
38
+ }
39
+ result
40
+ )
41
+
42
+ @driver.execute_script js
43
+ end
44
+
45
+ # iOS only. On Android uiautomator always returns an empty string for EditText password.
46
+ #
47
+ # Password character returned from value of UIASecureTextField
48
+ # @param length [Integer] the length of the password to generate
49
+ # @return [String] the returned string is of size length
50
+ def password length=1
51
+ '•' * length
52
+ end
53
+
54
+ def get_page element
55
+
56
+ def empty ele
57
+ (ele['name'] || ele['label'] || ele['value']) == nil
58
+ end
59
+
60
+ def fix_space s
61
+ # ints don't respond to force encoding
62
+ return s unless s.respond_to? :force_encoding
63
+ # char code 160 (name, label) vs 32 (value) will break comparison.
64
+ # convert string to binary and remove 160.
65
+ # \xC2\xA0
66
+ s.force_encoding('binary').gsub("\xC2\xA0".force_encoding('binary'), ' ') if s
67
+ end
68
+
69
+ unless empty(element)
70
+ puts "#{element['type']}"
71
+ name = fix_space element['name']
72
+ label = fix_space element['label']
73
+ value = fix_space element['value']
74
+
75
+ if name == label && name == value
76
+ puts " name, label, value: #{name}" if name
77
+ elsif name == label
78
+ puts " name, label: #{name}" if name
79
+ puts " value: #{value}" if value
80
+ elsif name == value
81
+ puts " name, value: #{name}" if name
82
+ puts " label: #{label}" if label
83
+ else
84
+ puts " name: #{name}" if name
85
+ puts " label: #{label}" if label
86
+ puts " value: #{value}" if value
87
+ end
88
+ end
89
+
90
+ children = element['children']
91
+ children.each { |c| get_page c } if children
92
+ nil
93
+ end
94
+
95
+ def page
96
+ get_page get_source
97
+ nil
98
+ end
99
+
100
+ def fast_duration
101
+ 0.5
102
+ end
103
+ end # module Appium::Ios
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ module Appium::Ios
3
+ # Implement useful features for element.
4
+ class Selenium::WebDriver::Element
5
+ # Cross platform way of entering text into a textfield
6
+ def type text
7
+ # enter text then tap window to hide the keyboard.
8
+ js = %Q(
9
+ au.getElement('#{self.ref}').setValue('#{text}');
10
+ au.lookup('window')[0].tap()
11
+ )
12
+ @driver.execute_script js
13
+ end
14
+ end
15
+ end
data/readme.md CHANGED
@@ -24,11 +24,18 @@ gem uninstall -aIx appium_lib ;\
24
24
  gem install --no-rdoc --no-ri appium_lib
25
25
  ```
26
26
 
27
- #### Run from Source
27
+ #### Simple Usage
28
28
 
29
- `pry -r ./lib/appium_lib.rb`
29
+ ```ruby
30
+ require 'appium_lib'
31
+
32
+ # Start a driver based on APP_PATH
33
+ Appium::Driver.new.start_driver
30
34
 
31
- Then `start_driver`
35
+ # Start an Android driver
36
+ apk = { 'APP_PATH' => ENV['APP_APK'] }
37
+ Appium::Driver.new(apk).start_driver
38
+ ```
32
39
 
33
40
  #### Documentation
34
41