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.
- checksums.yaml +8 -8
- data/Rakefile +15 -7
- data/appium_lib.gemspec +3 -3
- data/docs.md +7 -5
- data/lib/appium_lib.rb +34 -6
- data/lib/appium_lib/android/element/alert.rb +43 -0
- data/lib/appium_lib/android/element/generic.rb +94 -0
- data/lib/appium_lib/android/element/textfield.rb +43 -0
- data/lib/appium_lib/android/helper.rb +120 -0
- data/lib/appium_lib/android/patch.rb +10 -0
- data/lib/appium_lib/common/element/button.rb +83 -0
- data/lib/appium_lib/common/element/text.rb +44 -0
- data/lib/appium_lib/common/element/window.rb +9 -0
- data/lib/appium_lib/common/helper.rb +140 -0
- data/lib/appium_lib/common/patch.rb +83 -0
- data/lib/appium_lib/common/version.rb +6 -0
- data/lib/appium_lib/driver.rb +265 -0
- data/lib/appium_lib/ios/element/alert.rb +56 -0
- data/lib/appium_lib/ios/element/generic.rb +170 -0
- data/lib/appium_lib/ios/element/textfield.rb +90 -0
- data/lib/appium_lib/ios/helper.rb +103 -0
- data/lib/appium_lib/ios/patch.rb +15 -0
- data/readme.md +10 -3
- data/release_notes.md +8 -0
- metadata +19 -15
- data/lib/appium_lib/console.rb +0 -254
- data/lib/appium_lib/element/android/alert.rb +0 -45
- data/lib/appium_lib/element/android/generic.rb +0 -88
- data/lib/appium_lib/element/android/textfield.rb +0 -44
- data/lib/appium_lib/element/button.rb +0 -83
- data/lib/appium_lib/element/ios/alert.rb +0 -49
- data/lib/appium_lib/element/ios/generic.rb +0 -140
- data/lib/appium_lib/element/ios/textfield.rb +0 -93
- data/lib/appium_lib/element/text.rb +0 -43
- data/lib/appium_lib/element/window.rb +0 -12
- data/lib/appium_lib/helper.rb +0 -278
- data/lib/appium_lib/patch.rb +0 -90
- data/lib/appium_lib/version.rb +0 -4
data/lib/appium_lib/helper.rb
DELETED
@@ -1,278 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# Generic helper methods not specific
|
3
|
-
# to a particular tag name
|
4
|
-
|
5
|
-
# json and ap are required for the source method.
|
6
|
-
require 'json'
|
7
|
-
require 'ap' # awesome print
|
8
|
-
require 'timeout' # for wait
|
9
|
-
|
10
|
-
# iOS .name returns the accessibility attribute if it's set. if not set, the string value is used.
|
11
|
-
# Android .name returns the accessibility attribute and nothing if it's not set.
|
12
|
-
#
|
13
|
-
# .text should be cross platform so prefer that over name, unless both
|
14
|
-
# Android and iOS have proper accessibility attributes.
|
15
|
-
# .text and .value should be the same so use .text over .value.
|
16
|
-
#
|
17
|
-
# secure tag_name is iOS only because it can't be implemented using uiautomator for Android.
|
18
|
-
#
|
19
|
-
# find_element :text doesn't work so use XPath to find by text.
|
20
|
-
|
21
|
-
# Check every 0.5 seconds to see if block.call is true.
|
22
|
-
# Give up after 30 seconds.
|
23
|
-
# @param block [Block] the block to call
|
24
|
-
# @return [Object] the result of block.call
|
25
|
-
def wait &block
|
26
|
-
# Rescue Timeout::Error: execution expired
|
27
|
-
result = nil
|
28
|
-
timeout(30) { while(!(result = begin;block.call;rescue;end)) do; sleep(0.5) end }
|
29
|
-
result
|
30
|
-
end
|
31
|
-
|
32
|
-
# Presses the back button on Android.
|
33
|
-
# @return [void]
|
34
|
-
def back
|
35
|
-
$driver.navigate.back
|
36
|
-
end
|
37
|
-
|
38
|
-
# Get the element of type tag_name at matching index.
|
39
|
-
# @param tag_name [String] the tag name to find
|
40
|
-
# @param index [Integer] the index
|
41
|
-
# @return [Element] the found element of type tag_name
|
42
|
-
def ele_index tag_name, index
|
43
|
-
find_eles(tag_name)[index]
|
44
|
-
end
|
45
|
-
|
46
|
-
# Get all elements exactly matching tag name
|
47
|
-
# @param tag_name [String] the tag name to find
|
48
|
-
# @return [Array<Element>] the found elements of type tag_name
|
49
|
-
def find_eles tag_name
|
50
|
-
$driver.find_elements :tag_name, tag_name
|
51
|
-
end
|
52
|
-
|
53
|
-
# iOS only. Android uses uiautomator instead of uiautomation.
|
54
|
-
# Get an array of attribute values from elements exactly matching tag name.
|
55
|
-
# @param tag_name [String] the tag name to find
|
56
|
-
# @param attribute [String] the attribute to collect
|
57
|
-
# @result [Array<String>] an array of strings containing the attribute from found elements of type tag_name.
|
58
|
-
def find_eles_attr tag_name, attribute
|
59
|
-
# Use au.lookup(tag_name) instead of $(tag_name)
|
60
|
-
# See https://github.com/appium/appium/issues/214
|
61
|
-
js = %Q(
|
62
|
-
var eles = au.lookup('#{tag_name}');
|
63
|
-
var result = [];
|
64
|
-
for (var a = 0, length = eles.length; a < length; a++) {
|
65
|
-
result.push(eles[a].#{attribute}());
|
66
|
-
}
|
67
|
-
result
|
68
|
-
)
|
69
|
-
|
70
|
-
$driver.execute_script js
|
71
|
-
end
|
72
|
-
|
73
|
-
# iOS only. Android uses uiautomator instead of uiautomation.
|
74
|
-
# Get an array of attribute values from elements exactly matching tag name.
|
75
|
-
# @param tag_name_1 [String] the 1st tag name to find
|
76
|
-
# @param tag_name_2 [String] the 2nd tag name to find
|
77
|
-
# @param attribute [String] the attribute to collect
|
78
|
-
# @result [Array<String>] an array of strings containing the attribute from found elements of type tag_name.
|
79
|
-
def find_2_eles_attr tag_name_1, tag_name_2, attribute
|
80
|
-
# Use au.lookup(tag_name) instead of $(tag_name)
|
81
|
-
# See https://github.com/appium/appium/issues/214
|
82
|
-
js = %Q(
|
83
|
-
var eles = au.lookup('#{tag_name_1}');
|
84
|
-
eles = $(eles.concat(au.lookup('#{tag_name_2}')));
|
85
|
-
var result = [];
|
86
|
-
for (var a = 0, length = eles.length; a < length; a++) {
|
87
|
-
result.push(eles[a].#{attribute}());
|
88
|
-
}
|
89
|
-
result
|
90
|
-
)
|
91
|
-
|
92
|
-
$driver.execute_script js
|
93
|
-
end
|
94
|
-
|
95
|
-
# Get the first tag that exactly matches tag and text.
|
96
|
-
# @param tag [String] the tag name to match
|
97
|
-
# @param text [String] the text to exactly match
|
98
|
-
# @return [Element] the element of type tag exactly matching text
|
99
|
-
def find_ele_by_text tag, text
|
100
|
-
$driver.find_element :xpath, %Q(#{tag}[@text='#{text}'])
|
101
|
-
end
|
102
|
-
|
103
|
-
# Get all tags that exactly match tag and text.
|
104
|
-
# @param tag [String] the tag name to match
|
105
|
-
# @param text [String] the text to exactly match
|
106
|
-
# @return [Array<Element>] the elements of type tag exactly matching text
|
107
|
-
def find_eles_by_text tag, text
|
108
|
-
$driver.find_elements :xpath, %Q(#{tag}[@text='#{text}'])
|
109
|
-
end
|
110
|
-
|
111
|
-
# Get the first tag by attribute that exactly matches value.
|
112
|
-
# @param tag [String] the tag name to match
|
113
|
-
# @param attr [String] the attribute to compare
|
114
|
-
# @param value [String] the value of the attribute that the element must include
|
115
|
-
# @return [Element] the element of type tag who's attribute includes value
|
116
|
-
def find_ele_by_attr_include tag, attr, value
|
117
|
-
$driver.find_element :xpath, %Q(#{tag}[contains(@#{attr}, '#{value}')])
|
118
|
-
end
|
119
|
-
|
120
|
-
# Get tags by attribute that include value.
|
121
|
-
# @param tag [String] the tag name to match
|
122
|
-
# @param attr [String] the attribute to compare
|
123
|
-
# @param value [String] the value of the attribute that the element must include
|
124
|
-
# @return [Array<Element>] the elements of type tag who's attribute includes value
|
125
|
-
def find_eles_by_attr_include tag, attr, value
|
126
|
-
$driver.find_elements :xpath, %Q(#{tag}[contains(@#{attr}, '#{value}')])
|
127
|
-
end
|
128
|
-
|
129
|
-
# Get the first tag that includes text.
|
130
|
-
# @param tag [String] the tag name to match
|
131
|
-
# @param text [String] the text the element must include
|
132
|
-
# @return [Element] the element of type tag that includes text
|
133
|
-
# element.attribute(:text).include? text
|
134
|
-
def find_ele_by_text_include tag, text
|
135
|
-
find_ele_by_attr_include tag, :text, text
|
136
|
-
end
|
137
|
-
|
138
|
-
# Get the tags that include text.
|
139
|
-
# @param tag [String] the tag name to match
|
140
|
-
# @param text [String] the text the element must include
|
141
|
-
# @return [Array<Element>] the elements of type tag that includes text
|
142
|
-
# element.attribute(:text).include? text
|
143
|
-
def find_eles_by_text_include tag, text
|
144
|
-
find_eles_by_attr_include tag, :text, text
|
145
|
-
end
|
146
|
-
|
147
|
-
# Get the first tag that matches tag_name
|
148
|
-
# @param tag_name [String] the tag to match
|
149
|
-
# @return [Element]
|
150
|
-
def first_ele tag_name
|
151
|
-
tag = find_eles tag_name
|
152
|
-
tag = tag.first unless tag.nil?
|
153
|
-
end
|
154
|
-
|
155
|
-
# Get the last tag that matches tag_name
|
156
|
-
# @param tag_name [String] the tag to match
|
157
|
-
# @return [Element]
|
158
|
-
def last_ele tag_name
|
159
|
-
tag = find_eles tag_name
|
160
|
-
tag = tag.last unless tag.nil?
|
161
|
-
end
|
162
|
-
|
163
|
-
# Prints a JSON view of the current page
|
164
|
-
# @return [void]
|
165
|
-
def source
|
166
|
-
ap JSON.parse($driver.page_source)
|
167
|
-
end
|
168
|
-
|
169
|
-
# Gets a JSON view of the current page
|
170
|
-
# @return [JSON]
|
171
|
-
def get_source
|
172
|
-
JSON.parse($driver.page_source)
|
173
|
-
end
|
174
|
-
|
175
|
-
# iOS only. On Android uiautomator always returns an empty string for EditText password.
|
176
|
-
#
|
177
|
-
# Password character returned from value of UIASecureTextField
|
178
|
-
# @param length [Integer] the length of the password to generate
|
179
|
-
# @return [String] the returned string is of size length
|
180
|
-
def password length=1
|
181
|
-
'•' * length
|
182
|
-
end
|
183
|
-
|
184
|
-
# Android only.
|
185
|
-
def get_inspect
|
186
|
-
def run node
|
187
|
-
r = []
|
188
|
-
|
189
|
-
run_internal = lambda do |node|
|
190
|
-
if node.kind_of? Array
|
191
|
-
node.each { |node| run_internal.call node }
|
192
|
-
return
|
193
|
-
end
|
194
|
-
|
195
|
-
keys = node.keys
|
196
|
-
return if keys.empty?
|
197
|
-
|
198
|
-
obj = {}
|
199
|
-
obj.merge!( { desc: node["@content-desc"] } ) if keys.include?("@content-desc") && !node["@content-desc"].empty?
|
200
|
-
obj.merge!( { text: node["@text"] } ) if keys.include?("@text") && !node["@text"].empty?
|
201
|
-
obj.merge!( { class: node["@class"] } ) if keys.include?("@class") && !obj.empty?
|
202
|
-
|
203
|
-
r.push obj if !obj.empty?
|
204
|
-
run_internal.call node['node'] if keys.include?('node')
|
205
|
-
end
|
206
|
-
|
207
|
-
run_internal.call node
|
208
|
-
r
|
209
|
-
end
|
210
|
-
|
211
|
-
json = JSON.parse($driver.page_source)
|
212
|
-
node = json['hierarchy']
|
213
|
-
results = run node
|
214
|
-
|
215
|
-
out = ''
|
216
|
-
results.each { |e|
|
217
|
-
out += e[:class].split('.').last + "\n"
|
218
|
-
out += " text: #{e[:text]}\n" unless e[:text].nil?
|
219
|
-
out += " name: #{e[:desc]}\n" unless e[:desc].nil?
|
220
|
-
}
|
221
|
-
out
|
222
|
-
end if $os == :android
|
223
|
-
|
224
|
-
# Android only. Intended for use with console.
|
225
|
-
# Inspects and prints the current page.
|
226
|
-
def page
|
227
|
-
puts get_inspect
|
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
|
278
|
-
end
|
data/lib/appium_lib/patch.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# Implement useful features for element.
|
2
|
-
class Selenium::WebDriver::Element
|
3
|
-
# Note: For testing .text should be used over value, and name.
|
4
|
-
|
5
|
-
# Fixes NoMethodError: undefined method `value' for #<Selenium::WebDriver::Element:0x..fa4a9148235390a44 id="1">
|
6
|
-
def value
|
7
|
-
self.attribute :value
|
8
|
-
end
|
9
|
-
|
10
|
-
# Fixes NoMethodError: undefined method `name' for #<Selenium::WebDriver::Element
|
11
|
-
def name
|
12
|
-
self.attribute :name
|
13
|
-
end
|
14
|
-
|
15
|
-
# Use tag_name to get element's type.
|
16
|
-
#
|
17
|
-
# Tag name appears to be the same as type.
|
18
|
-
#
|
19
|
-
# Fixes Selenium::WebDriver::Error::UnknownError: Not yet implemented
|
20
|
-
def tag_name
|
21
|
-
self.attribute :type
|
22
|
-
end
|
23
|
-
|
24
|
-
# Cross platform way of entering text into a textfield
|
25
|
-
def type text
|
26
|
-
# enter text then tap window to hide the keyboard.
|
27
|
-
js = %Q(
|
28
|
-
au.getElement('#{self.ref}').setValue('#{text}');
|
29
|
-
au.lookup('window')[0].tap()
|
30
|
-
)
|
31
|
-
$driver.execute_script js
|
32
|
-
end if $os == :ios
|
33
|
-
|
34
|
-
# Cross platform way of entering text into a textfield
|
35
|
-
def type text
|
36
|
-
self.send_keys text
|
37
|
-
end if $os == :android
|
38
|
-
|
39
|
-
# For use with mobile tap.
|
40
|
-
#
|
41
|
-
# $driver.execute_script 'mobile: tap', :x => 0.0, :y => 0.98
|
42
|
-
#
|
43
|
-
# https://github.com/appium/appium/wiki/Automating-mobile-gestures
|
44
|
-
# @return [OpenStruct] the relative x, y in a struct. ex: { x: 0.50, y: 0.20 }
|
45
|
-
def location_rel
|
46
|
-
xy = self.location
|
47
|
-
w = window_size
|
48
|
-
OpenStruct.new( x: xy.x.to_f / w.width.to_f,
|
49
|
-
y: xy.y.to_f / w.height.to_f )
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Print JSON posted to Appium
|
54
|
-
#
|
55
|
-
# Requires from lib/selenium/webdriver/remote.rb
|
56
|
-
require 'selenium/webdriver/remote/capabilities'
|
57
|
-
require 'selenium/webdriver/remote/bridge'
|
58
|
-
require 'selenium/webdriver/remote/server_error'
|
59
|
-
require 'selenium/webdriver/remote/response'
|
60
|
-
require 'selenium/webdriver/remote/commands'
|
61
|
-
require 'selenium/webdriver/remote/http/common'
|
62
|
-
require 'selenium/webdriver/remote/http/default'
|
63
|
-
|
64
|
-
# Show http calls to the Selenium server.
|
65
|
-
#
|
66
|
-
# Invaluable for debugging.
|
67
|
-
module Selenium::WebDriver::Remote
|
68
|
-
class Bridge
|
69
|
-
# Code from lib/selenium/webdriver/remote/bridge.rb
|
70
|
-
def raw_execute(command, opts = {}, command_hash = nil)
|
71
|
-
verb, path = COMMANDS[command] || raise(ArgumentError, "unknown command: #{command.inspect}")
|
72
|
-
path = path.dup
|
73
|
-
|
74
|
-
path[':session_id'] = @session_id if path.include?(":session_id")
|
75
|
-
|
76
|
-
begin
|
77
|
-
opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
|
78
|
-
rescue IndexError
|
79
|
-
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
|
80
|
-
end
|
81
|
-
|
82
|
-
puts verb
|
83
|
-
puts path
|
84
|
-
puts command_hash.to_json
|
85
|
-
|
86
|
-
puts "-> #{verb.to_s.upcase} #{path}" if $DEBUG
|
87
|
-
http.call verb, path, command_hash
|
88
|
-
end # def
|
89
|
-
end # class
|
90
|
-
end if defined?(Pry)# module
|
data/lib/appium_lib/version.rb
DELETED