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
@@ -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