appium_lib 0.0.27 → 0.0.28
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.
- checksums.yaml +8 -8
- data/appium_lib.gemspec +1 -1
- data/docs.md +229 -0
- data/lib/appium_lib/console.rb +4 -1
- data/lib/appium_lib/element/android/generic.rb +42 -1
- data/lib/appium_lib/element/ios/generic.rb +100 -15
- data/lib/appium_lib/helper.rb +50 -1
- data/lib/appium_lib/version.rb +2 -2
- data/readme.md +5 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZWUwOTFmNDhjOWQ4YzBhYTVhZmQ4N2YyZDUxZWUyYmE4YTk1ZmY5OQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZGU2MjI1OWE0M2JjNDZjNzRhYzBiNGE0NWYyYjhlYmViMGI4NjQwMQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTM0NzQxNzdkNjc3YzhlMDJmMTEzNTViNzRmN2ZhNjFmMThhMGMzNzhmNGVk
|
10
|
+
NWRiMTg4M2IyZTE5ZWM1ZDdlM2NkNTEzMTJlMWVhYTU0OTczNDU4NDEyZGRk
|
11
|
+
MjVjYzY0M2ViYjg2MThkNGVmOTJlNjQxMDI0MDYwYjAwNmM4MmQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MzYyNTk0NmMwMDlmYmEzM2RjN2Q0MzMwNDQ2ZDM5MTU5MTlmYzgwMzY0MWEw
|
14
|
+
NmY5NzA4OWYzYTAyMGVhMDBkMzYzNDJhMDFhYzIwYTI0OWI5Nzc3YzRiMjNj
|
15
|
+
YWU1ZmI3ZWUzZWJkOGY3MWUwMjRlZDhkZTRlZWViNjZiNDY3MjI=
|
data/appium_lib.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.homepage = 'https://github.com/appium/ruby_lib' # published as appium_lib
|
25
25
|
s.require_paths = [ 'lib' ]
|
26
26
|
|
27
|
-
s.add_runtime_dependency 'selenium-webdriver', '~> 2.
|
27
|
+
s.add_runtime_dependency 'selenium-webdriver', '~> 2.32.0'
|
28
28
|
s.add_runtime_dependency 'awesome_print', '~> 1.1.0'
|
29
29
|
|
30
30
|
s.add_development_dependency 'rake', '~> 10.0.3'
|
data/docs.md
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
#### Documentation
|
2
|
+
|
3
|
+
- find_elements returns an empty array [] when no elements are found.
|
4
|
+
|
5
|
+
##### [app_lib on rubydoc.info](http://www.rubydoc.info/github/appium/ruby_lib/master/toplevel)
|
6
|
+
|
7
|
+
- [iOS UI Automation](http://developer.apple.com/library/ios/#documentation/DeveloperTools/Reference/UIAutomationRef/_index.html) Example use `@driver.execute_script "UIATarget.localTarget().frontMostApp().mainWindow().rect()"
|
8
|
+
`
|
9
|
+
- [Android UIAutomator](http://developer.android.com/tools/help/uiautomator/index.html)
|
10
|
+
- [UiSelector.java](https://android.googlesource.com/platform/frameworks/testing/+/master/uiautomator/library/src/com/android/uiautomator/core/UiSelector.java)
|
11
|
+
- [Ruby selenium-webdriver](http://selenium.googlecode.com/svn/trunk/docs/api/rb/index.html)
|
12
|
+
- [Appium](https://github.com/appium/appium/blob/master/README.md)
|
13
|
+
- [Appium extension](https://github.com/appium/appium/wiki/Automating-mobile-gestures)
|
14
|
+
- [mechanic names of elements](https://github.com/jaykz52/mechanic/blob/8c490e1d225f384847e47ffdafb47cc2248bb96c/src/mechanic-core.js#L28)
|
15
|
+
- [All methods supported by Appium](https://github.com/appium/appium/wiki/JSON-Wire-Protocol:-Supported-Methods)
|
16
|
+
- [Appium's mobile gesture docs](https://github.com/appium/appium/wiki/Automating-mobile-gestures)
|
17
|
+
-
|
18
|
+
Example use of Appium's mobile gesture.
|
19
|
+
|
20
|
+
> @driver.execute_script 'mobile: tap', :x => 0, :y => 500
|
21
|
+
|
22
|
+
`console.rb` uses some code from [simple_test.rb](
|
23
|
+
https://github.com/appium/appium/blob/82995f47408530c80c3376f4e07a1f649d96ba22/sample-code/examples/ruby/simple_test.rb) and is released under the [same license](https://github.com/appium/appium/blob/c58eeb66f2d6fa3b9a89d188a2e657cca7cb300f/LICENSE) as Appium. The [Accessibility Inspector](https://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/iPhoneAccessibility/Testing_Accessibility/Testing_Accessibility.html) is helpful for discovering button names and textfield values.
|
24
|
+
|
25
|
+
--
|
26
|
+
|
27
|
+
Tag Name | UIA
|
28
|
+
--:|:--
|
29
|
+
button | UIAButton
|
30
|
+
textfield | UIATextField
|
31
|
+
secure | UIASecureTextField
|
32
|
+
text | UIAStaticText
|
33
|
+
|
34
|
+
|
35
|
+
--
|
36
|
+
|
37
|
+
#### generic
|
38
|
+
|
39
|
+
- `source` Prints a JSON view of the current page.
|
40
|
+
- `page` Prints the content descriptions and text values on the current page.
|
41
|
+
- `(Element) find(value)` Returns the first element that contains value.
|
42
|
+
- `(Element) finds(value)` Returns all elements containing value (iOS only for now).
|
43
|
+
- `(Element) name(name)` Returns the first element containing name. Android name is the content description.
|
44
|
+
iOS uses accessibility label with a fallback to text.
|
45
|
+
- `(Array<Element>) names(name)` Returns all elements containing name.
|
46
|
+
- `(Element) text(text)` Returns the first element containing text.
|
47
|
+
- `(Array<Element>) texts(text)` Returns all elements containing text.
|
48
|
+
--
|
49
|
+
|
50
|
+
#### alert
|
51
|
+
0. `(void) alert_accept` Accept the alert.
|
52
|
+
0. `(String) alert_accept_text` Get the text of the alert's accept button.
|
53
|
+
0. `(void) alert_click(value)` iOS only Tap the alert button identified by value.
|
54
|
+
0. `(void) alert_dismiss` Dismiss the alert.
|
55
|
+
0. `(String) alert_dismiss_text` Get the text of the alert's dismiss button.
|
56
|
+
0. `(String) alert_text` Get the alert message text.
|
57
|
+
|
58
|
+
#### button
|
59
|
+
0. `(Button) button(index)` Find a button by index.
|
60
|
+
0. `(Button) button(text, number = 0)` Find a button by text and optionally number.
|
61
|
+
0. `(Button) button_include(text)` Get the first button that includes text.
|
62
|
+
0. `(Array<String>, Array<Buttons>) buttons(text = nil)` Get an array of button texts or button elements if text is provided.
|
63
|
+
0. `(Array<Button>) buttons_include(text)` Get all buttons that include text.
|
64
|
+
0. `(Button) first_button` Get the first button element.
|
65
|
+
0. `(Button) last_button` Get the last button element.
|
66
|
+
|
67
|
+
#### textfield
|
68
|
+
0. `(Textfield) textfield(index)` Find a textfield by index.
|
69
|
+
0. `(Array<Textfield>) e_textfields` Get an array of textfield elements.
|
70
|
+
0. `(Textfield) first_textfield` Get the first textfield element.
|
71
|
+
0. `(Textfield) last_textfield` Get the last textfield element.
|
72
|
+
0. `(Textfield) textfield(text)` Get the first textfield that matches text.
|
73
|
+
0. `(Textfield) textfield_include(text)` Get the first textfield that includes text.
|
74
|
+
0. `(Array<String>) textfields` Get an array of textfield texts.
|
75
|
+
|
76
|
+
#### text
|
77
|
+
|
78
|
+
The Static Text methods have been prefixed with `s_` to avoid conflicting with the generic text methods.
|
79
|
+
|
80
|
+
0. `(Text) s_text(index)` Find a text by index.
|
81
|
+
0. `(Array<Text>) s_e_texts` Get an array of text elements.
|
82
|
+
0. `(Text) s_first_text` Get the first text element.
|
83
|
+
0. `(Text) s_last_text` Get the last text element.
|
84
|
+
0. `(Text) s_text(text)` Get the first element that matches text.
|
85
|
+
0. `(Text) s_text_include(text)` Get the first textfield that includes text.
|
86
|
+
0. `(Array<String>) s_texts` Get an array of text texts.
|
87
|
+
|
88
|
+
#### window
|
89
|
+
0. `(Object) window_size` Get the window's size.
|
90
|
+
|
91
|
+
--
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
e.name # button, text
|
95
|
+
e.value # secure, textfield
|
96
|
+
e.type
|
97
|
+
e.tag_name # calls .type (patch.rb)
|
98
|
+
e.text
|
99
|
+
e.size
|
100
|
+
e.location
|
101
|
+
e.rel_location
|
102
|
+
e.click
|
103
|
+
e.send_keys 'keys to send'
|
104
|
+
e.set_value 'value to set' # ruby_console specific
|
105
|
+
|
106
|
+
# alert example without helper methods
|
107
|
+
alert = $driver.switch_to.alert
|
108
|
+
alert.text
|
109
|
+
alert.accept
|
110
|
+
alert.dismiss
|
111
|
+
|
112
|
+
# Secure textfield example.
|
113
|
+
#
|
114
|
+
# Find using default value
|
115
|
+
s = secure 'Password'
|
116
|
+
# Enter password
|
117
|
+
s.send_keys 'hello'
|
118
|
+
# Check value
|
119
|
+
s.value == password('hello'.length)
|
120
|
+
```
|
121
|
+
|
122
|
+
[routing.js](https://github.com/appium/appium/blob/master/app/routing.js#L69) lists not yet implemented end points.
|
123
|
+
|
124
|
+
--
|
125
|
+
|
126
|
+
#### Driver
|
127
|
+
|
128
|
+
`start_driver` will restart the driver.
|
129
|
+
|
130
|
+
`x` will quit the driver and exit Pry.
|
131
|
+
|
132
|
+
`execute_script` calls `$driver.execute_script`
|
133
|
+
|
134
|
+
`find_element` calls `$driver.find_element`
|
135
|
+
|
136
|
+
`find_elements` calls `$driver.find_elements`
|
137
|
+
|
138
|
+
`mobile :swipe, endX: 100, endY: 100, duration: 0.01` calls `$driver.execute_script 'mobile: swipe', endX: 100, endY: 100, duration: 0.01`
|
139
|
+
|
140
|
+
`no_wait` will set implicit wait to 0. `$driver.manage.timeouts.implicit_wait = 0`
|
141
|
+
|
142
|
+
`set_wait` will set implicit wait to default 30 seconds. `$driver.manage.timeouts.implicit_wait = 30`
|
143
|
+
|
144
|
+
`set_wait(timeout_seconds)` will set implicit wait to desired timeout. `$driver.manage.timeouts.implicit_wait = timeout`
|
145
|
+
|
146
|
+
.click to tap an element.
|
147
|
+
.send_keys to type on an element.
|
148
|
+
|
149
|
+
#### Raw UIAutomation
|
150
|
+
|
151
|
+
`execute_script "au.lookup('button')[0].tap()"` is the same as
|
152
|
+
`execute_script 'UIATarget.localTarget().frontMostApp().buttons()[0].tap()'`
|
153
|
+
|
154
|
+
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.
|
156
|
+
|
157
|
+
Advanced au.
|
158
|
+
|
159
|
+
In this example we lookup two tags, combine the results, wrap with $, and then return the elements.
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
s = %(
|
163
|
+
var t = au.lookup('textfield');
|
164
|
+
var s = au.lookup('secure');
|
165
|
+
var r = $(t.concat(s));
|
166
|
+
au._returnElems(r);
|
167
|
+
)
|
168
|
+
|
169
|
+
execute_script s
|
170
|
+
```
|
171
|
+
|
172
|
+
#### XPath
|
173
|
+
|
174
|
+
See [#194](https://github.com/appium/appium/pull/194/files) for details.
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
find_element :xpath, 'button'
|
178
|
+
find_elements :xpath, 'button'
|
179
|
+
|
180
|
+
find_element :xpath, 'button[@name="Sign In"]'
|
181
|
+
find_elements :xpath, 'button[@name="Sign In"]'
|
182
|
+
|
183
|
+
find_element :xpath, 'button[contains(@name, "Sign In")]'
|
184
|
+
find_elements :xpath, 'button[contains(@name, "Sign")]'
|
185
|
+
|
186
|
+
find_element :xpath, 'textfield[@value="Email"]'
|
187
|
+
find_element :xpath, 'textfield[contains(@value, "Email")]'
|
188
|
+
|
189
|
+
find_element :xpath, 'text[contains(@name, "Reset")]'
|
190
|
+
find_elements :xpath, 'text[contains(@name, "agree")]'
|
191
|
+
```
|
192
|
+
|
193
|
+
#### Cucumber Sauce Integration
|
194
|
+
|
195
|
+
Reset after each test and when done report the result to Sauce after quiting the driver.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
require 'rest_client' # https://github.com/archiloque/rest-client
|
199
|
+
require 'json' # for .to_json
|
200
|
+
|
201
|
+
$passed = true
|
202
|
+
|
203
|
+
After do |scenario|
|
204
|
+
# Reset scenario unless the feature was tagged @keep
|
205
|
+
$driver.execute_script 'mobile: reset' unless scenario.feature.source_tag_names.include? '@keep'
|
206
|
+
|
207
|
+
if $passed
|
208
|
+
$passed = false if scenario.failed?
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
at_exit do
|
213
|
+
ID = $driver.send(:bridge).session_id
|
214
|
+
$driver.quit
|
215
|
+
|
216
|
+
if !SAUCE_USERNAME.nil? && !SAUCE_ACCESS_KEY.nil?
|
217
|
+
URL = "https://#{SAUCE_USERNAME}:#{SAUCE_ACCESS_KEY}@saucelabs.com/rest/v1/#{SAUCE_USERNAME}/jobs/#{ID}"
|
218
|
+
|
219
|
+
# Keep trying until passed is set correctly. Give up after 30 seconds.
|
220
|
+
wait do
|
221
|
+
response = RestClient.put URL, { 'passed' => $passed }.to_json, :content_type => :json, :accept => :json
|
222
|
+
response = JSON.parse(response)
|
223
|
+
|
224
|
+
# Check that the server responded with the right value.
|
225
|
+
response['passed'] == $passed
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
```
|
data/lib/appium_lib/console.rb
CHANGED
@@ -112,7 +112,10 @@ end
|
|
112
112
|
def absolute_app_path
|
113
113
|
raise 'APP_PATH environment variable not set!' if APP_PATH.nil?
|
114
114
|
return APP_PATH if APP_PATH.match(/^http/) # public URL for Sauce
|
115
|
-
|
115
|
+
if APP_PATH.match(/^\//) # absolute file path
|
116
|
+
raise "App doesn't exist. #{APP_PATH}" unless File.exist? APP_PATH
|
117
|
+
return APP_PATH
|
118
|
+
end
|
116
119
|
file = File.join(File.dirname(__FILE__), APP_PATH)
|
117
120
|
raise "App doesn't exist #{file}" unless File.exist? file
|
118
121
|
file
|
@@ -10,6 +10,47 @@ find_element :name by default uses a partial case insensitive match.
|
|
10
10
|
On iOS the default is an exact name match.
|
11
11
|
=end
|
12
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
|
+
# s.classNameMatches('(?i).*button.*').descriptionContains(value);
|
44
|
+
[ [26, '(?i).*button.*'], [7, val] ],
|
45
|
+
# s.classNameMatches('(?i).*button.*').textContains(value);
|
46
|
+
[ [26, '(?i).*button.*'], [3, val] ],
|
47
|
+
# s.descriptionContains(value);
|
48
|
+
[ [7, val] ],
|
49
|
+
# s.textContains(value);
|
50
|
+
[ [3, val] ]
|
51
|
+
mobile :find, args
|
52
|
+
end
|
53
|
+
|
13
54
|
# Return the first element matching text.
|
14
55
|
# @param text [String] the text to search for
|
15
56
|
# @return [Element] the first matching element
|
@@ -44,4 +85,4 @@ def names name
|
|
44
85
|
$driver.find_elements :name, name
|
45
86
|
end
|
46
87
|
|
47
|
-
end # if $os == :android
|
88
|
+
end # if $os == :android
|
@@ -5,17 +5,76 @@ name, names, text, text should match substring and case insensitive.
|
|
5
5
|
|
6
6
|
iOS .name() is the accessibility attribute. If not defined, then .label() is used instead.
|
7
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
|
8
21
|
=end
|
9
22
|
|
23
|
+
def first_ele_js predicate
|
24
|
+
# returnElems requires a wrapped $(element).
|
25
|
+
# set to empty array when length is zero to prevent hang.
|
26
|
+
#
|
27
|
+
# UIAElementNil when not matched
|
28
|
+
#
|
29
|
+
# 1. secureTextFields
|
30
|
+
# 2. textFields
|
31
|
+
# 3. buttons
|
32
|
+
# 4. elements
|
33
|
+
%Q(
|
34
|
+
function isNil( a ) {
|
35
|
+
return a.type() === 'UIAElementNil';
|
36
|
+
}
|
37
|
+
|
38
|
+
var w = au.mainWindow;
|
39
|
+
var search = #{predicate};
|
40
|
+
var a = w.secureTextFields().firstWithPredicate(search);
|
41
|
+
if ( isNil(a) ) {
|
42
|
+
a = w.textFields().firstWithPredicate(search);
|
43
|
+
if ( isNil(a) ) {
|
44
|
+
a = w.buttons().firstWithPredicate(search);
|
45
|
+
if ( isNil(a) ) {
|
46
|
+
a = w.elements().firstWithPredicate(search);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
if ( a.length === 0 ) {
|
52
|
+
a = [];
|
53
|
+
}
|
54
|
+
|
55
|
+
au._returnElems($(a));
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def all_ele_js predicate
|
60
|
+
%Q(
|
61
|
+
var w = au.mainWindow;
|
62
|
+
var search = #{predicate};
|
63
|
+
var a = w.elements().withPredicate(search).toArray();
|
64
|
+
|
65
|
+
if ( a.length === 0 ) {
|
66
|
+
a = [];
|
67
|
+
}
|
68
|
+
|
69
|
+
au._returnElems($(a));
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
10
73
|
# Return the first element matching text.
|
11
74
|
# @param text [String] the text to search for
|
12
75
|
# @return [Element] the first matching element
|
13
|
-
def
|
14
|
-
#
|
15
|
-
js = %Q(
|
16
|
-
var element = $(au.mainWindow.elements().firstWithPredicate("name contains[c] '#{text}'"));
|
17
|
-
au._returnElems(element);
|
18
|
-
)
|
76
|
+
def find text
|
77
|
+
js = first_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'"
|
19
78
|
|
20
79
|
execute_script(js).first
|
21
80
|
end
|
@@ -23,13 +82,32 @@ end
|
|
23
82
|
# Return all elements matching text.
|
24
83
|
# @param text [String] the text to search for
|
25
84
|
# @return [Array<Element>] all matching elements
|
26
|
-
def
|
85
|
+
def finds text
|
27
86
|
# returnElems requires a wrapped $(element).
|
28
87
|
# must call toArray when using withPredicate instead of firstWithPredicate.
|
29
|
-
js =
|
30
|
-
|
31
|
-
|
32
|
-
|
88
|
+
js = all_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}' || value contains[c] '#{text}'"
|
89
|
+
|
90
|
+
execute_script js
|
91
|
+
end
|
92
|
+
|
93
|
+
# Return the first element matching text.
|
94
|
+
# @param text [String] the text to search for
|
95
|
+
# @return [Element] the first matching element
|
96
|
+
def text text
|
97
|
+
# TODO: Use XPath index once it's implemented
|
98
|
+
# https://github.com/appium/appium/issues/295
|
99
|
+
js = first_ele_js "value contains[c] '#{text}'"
|
100
|
+
|
101
|
+
execute_script(js).first
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return all elements matching text.
|
105
|
+
# @param text [String] the text to search for
|
106
|
+
# @return [Array<Element>] all matching elements
|
107
|
+
def texts text
|
108
|
+
# XPath //* is not implemented on iOS
|
109
|
+
# https://github.com/appium/appium/issues/430
|
110
|
+
js = all_ele_js "value contains[c] '#{text}'"
|
33
111
|
|
34
112
|
execute_script js
|
35
113
|
end
|
@@ -39,8 +117,10 @@ end
|
|
39
117
|
# on iOS name is the accessibility label or the text.
|
40
118
|
# @param name [String] the name to search for
|
41
119
|
# @return [Element] the first matching element
|
42
|
-
def name
|
43
|
-
text
|
120
|
+
def name text
|
121
|
+
js = first_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}'"
|
122
|
+
|
123
|
+
execute_script(js).first
|
44
124
|
end
|
45
125
|
|
46
126
|
# Return all elements matching name.
|
@@ -48,8 +128,13 @@ end
|
|
48
128
|
# on iOS name is the accessibility label or the text.
|
49
129
|
# @param name [String] the name to search for
|
50
130
|
# @return [Array<Element>] all matching elements
|
51
|
-
def names
|
52
|
-
|
131
|
+
def names text
|
132
|
+
# find_elements :name is not the same as on Android.
|
133
|
+
# it's case sensitive and exact on iOS and not on Android.
|
134
|
+
# https://github.com/appium/appium/issues/379
|
135
|
+
js = all_ele_js "name contains[c] '#{text}' || label contains[c] '#{text}''"
|
136
|
+
|
137
|
+
execute_script js
|
53
138
|
end
|
54
139
|
|
55
140
|
end # if ios
|
data/lib/appium_lib/helper.rb
CHANGED
@@ -219,11 +219,60 @@ def get_inspect
|
|
219
219
|
out += " name: #{e[:desc]}\n" unless e[:desc].nil?
|
220
220
|
}
|
221
221
|
out
|
222
|
-
end
|
222
|
+
end if $os == :android
|
223
223
|
|
224
224
|
# Android only. Intended for use with console.
|
225
225
|
# Inspects and prints the current page.
|
226
226
|
def page
|
227
227
|
puts get_inspect
|
228
228
|
nil
|
229
|
+
end if $os == :android
|
230
|
+
|
231
|
+
def page element
|
232
|
+
|
233
|
+
def empty ele
|
234
|
+
(ele['name'] || ele['label'] || ele['value']) == nil
|
235
|
+
end
|
236
|
+
|
237
|
+
def fix_space s
|
238
|
+
# char code 160 (name, label) vs 32 (value) will break comparison.
|
239
|
+
# convert string to binary and remove 160.
|
240
|
+
# \xC2\xA0
|
241
|
+
s.force_encoding('binary').gsub("\xC2\xA0".force_encoding('binary'), ' ') if s
|
242
|
+
end
|
243
|
+
|
244
|
+
if ! empty( element )
|
245
|
+
puts "#{element['type']}"
|
246
|
+
name = fix_space element['name']
|
247
|
+
label = fix_space element['label']
|
248
|
+
value = fix_space element['value']
|
249
|
+
|
250
|
+
if name == label && name == value
|
251
|
+
puts " name, label, value: #{name}" if name
|
252
|
+
elsif name == label
|
253
|
+
puts " name, label: #{name}" if name
|
254
|
+
puts " value: #{value}" if value
|
255
|
+
elsif name == value
|
256
|
+
puts " name, value: #{name}" if name
|
257
|
+
puts " label: #{label}" if label
|
258
|
+
else
|
259
|
+
puts " name: #{name}" if name
|
260
|
+
puts " label: #{label}" if label
|
261
|
+
puts " value: #{value}" if value
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
children = element['children']
|
266
|
+
children.each { |c| page c } if children
|
267
|
+
nil
|
268
|
+
end if $os == :ios
|
269
|
+
|
270
|
+
# JavaScript code from https://github.com/appium/appium/blob/master/app/android.js
|
271
|
+
#
|
272
|
+
# Math.round((duration * 1000) / 200)
|
273
|
+
# (.20 * 1000) / 200 = 1
|
274
|
+
#
|
275
|
+
# We want steps to be exactly 1. If it's zero then a tap is used instead of a swipe.
|
276
|
+
def fast_duration
|
277
|
+
0.20
|
229
278
|
end
|
data/lib/appium_lib/version.rb
CHANGED
data/readme.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
- [appium_lib on RubyGems](https://rubygems.org/gems/appium_lib)
|
4
4
|
- [Documentation for appium_lib](http://www.rubydoc.info/github/appium/ruby_lib/master/frames)
|
5
|
+
- [Appium Ruby Console](https://github.com/appium/ruby_console)
|
5
6
|
|
6
7
|
Helper methods for writing cross platform (iPad, iPhone, Android) tests in Ruby using Appium.
|
7
|
-
There's also an [Appium Ruby Console](https://github.com/appium/ruby_console) which uses this lib.
|
8
8
|
|
9
9
|
Make sure you're using Ruby 1.9.3+ with upgraded rubygems and bundler.
|
10
10
|
|
@@ -29,3 +29,7 @@ gem install --no-rdoc --no-ri appium_lib
|
|
29
29
|
`pry -r ./lib/appium_lib.rb`
|
30
30
|
|
31
31
|
Then `start_driver`
|
32
|
+
|
33
|
+
#### Documentation
|
34
|
+
|
35
|
+
See [docs.md](https://github.com/appium/ruby_lib/blob/master/docs.md)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appium_lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.28
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- code@bootstraponline.com
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: selenium-webdriver
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.32.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
26
|
+
version: 2.32.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: awesome_print
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- LICENSE-2.0.txt
|
65
65
|
- Rakefile
|
66
66
|
- appium_lib.gemspec
|
67
|
+
- docs.md
|
67
68
|
- lib/appium_lib.rb
|
68
69
|
- lib/appium_lib/console.rb
|
69
70
|
- lib/appium_lib/element/android/alert.rb
|