appium_lib 4.1.0 → 5.0.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.
- checksums.yaml +4 -4
- data/android_tests/api.apk +0 -0
- data/android_tests/lib/android/specs/android/element/text.rb +2 -2
- data/android_tests/lib/android/specs/android/helper.rb +2 -2
- data/android_tests/lib/android/specs/common/device.rb +10 -28
- data/android_tests/lib/android/specs/common/device_touchaction.rb +29 -0
- data/android_tests/lib/android/specs/common/helper.rb +4 -4
- data/android_tests/lib/android/specs/common/patch.rb +2 -2
- data/android_tests/lib/android/specs/driver.rb +11 -1
- data/android_tests/lib/android/specs/install.rb +7 -6
- data/appium_lib.gemspec +2 -10
- data/contributing.md +23 -0
- data/docs/android_docs.md +274 -217
- data/docs/docs.md +28 -0
- data/docs/ios_docs.md +372 -212
- data/ios_tests/UICatalog.app/Info.plist +0 -0
- data/ios_tests/appium.txt +2 -1
- data/ios_tests/lib/ios/specs/common/helper.rb +12 -21
- data/ios_tests/lib/ios/specs/common/web_context.rb +7 -2
- data/ios_tests/lib/ios/specs/device/device.rb +16 -10
- data/ios_tests/lib/ios/specs/driver.rb +13 -2
- data/ios_tests/lib/ios/specs/ios/element/button.rb +5 -4
- data/ios_tests/lib/ios/specs/ios/element/generic.rb +2 -2
- data/ios_tests/lib/ios/specs/ios/element/text.rb +11 -7
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +9 -9
- data/lib/appium_lib.rb +1 -17
- data/lib/appium_lib/android/helper.rb +55 -5
- data/lib/appium_lib/common/helper.rb +26 -30
- data/lib/appium_lib/common/patch.rb +12 -0
- data/lib/appium_lib/common/version.rb +2 -2
- data/lib/appium_lib/device/device.rb +83 -12
- data/lib/appium_lib/driver.rb +26 -14
- data/lib/appium_lib/ios/element/button.rb +4 -4
- data/lib/appium_lib/ios/element/generic.rb +4 -4
- data/lib/appium_lib/ios/element/text.rb +4 -4
- data/lib/appium_lib/ios/element/textfield.rb +41 -22
- data/lib/appium_lib/ios/helper.rb +216 -47
- data/lib/appium_lib/ios/patch.rb +0 -19
- data/readme.md +1 -0
- data/release_notes.md +64 -0
- metadata +7 -5
Binary file
|
data/ios_tests/appium.txt
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# rake ios[common/helper]
|
2
2
|
describe 'common/helper.rb' do
|
3
|
+
|
3
4
|
def before_first
|
4
5
|
screen.must_equal catalog
|
5
6
|
end
|
@@ -91,11 +92,11 @@ must_not_raise is a no-op.
|
|
91
92
|
end
|
92
93
|
|
93
94
|
t 'xpaths' do
|
94
|
-
xpaths('//UIAStaticText').length.must_equal
|
95
|
+
xpaths('//UIAStaticText').length.must_equal 25
|
95
96
|
end
|
96
97
|
|
97
98
|
def uibutton_text
|
98
|
-
'Buttons
|
99
|
+
'Buttons'
|
99
100
|
end
|
100
101
|
|
101
102
|
t 'ele_index' do
|
@@ -126,7 +127,7 @@ must_not_raise is a no-op.
|
|
126
127
|
|
127
128
|
t 'find_ele_by_attr_include' do
|
128
129
|
el_text = find_ele_by_attr_include('UIAStaticText', :name, 'button').text
|
129
|
-
el_text.must_equal
|
130
|
+
el_text.must_equal uibutton_text
|
130
131
|
|
131
132
|
el_name = find_ele_by_attr_include('UIAStaticText', :name, 'button').name
|
132
133
|
el_name.must_equal uibutton_text
|
@@ -134,7 +135,7 @@ must_not_raise is a no-op.
|
|
134
135
|
|
135
136
|
t 'find_eles_by_attr_include' do
|
136
137
|
ele_count = find_eles_by_attr_include('UIAStaticText', :name, 'e').length
|
137
|
-
ele_count.must_equal
|
138
|
+
ele_count.must_equal 19
|
138
139
|
end
|
139
140
|
|
140
141
|
t 'first_ele' do
|
@@ -143,8 +144,8 @@ must_not_raise is a no-op.
|
|
143
144
|
|
144
145
|
t 'last_ele' do
|
145
146
|
el = last_ele('UIAStaticText')
|
146
|
-
el.text.must_equal ''
|
147
|
-
el.name.must_equal 'Transitions
|
147
|
+
el.text.must_equal 'Transitions'
|
148
|
+
el.name.must_equal 'Transitions'
|
148
149
|
end
|
149
150
|
|
150
151
|
# t 'source' do # tested by get_source
|
@@ -158,24 +159,14 @@ must_not_raise is a no-op.
|
|
158
159
|
end
|
159
160
|
|
160
161
|
t 'invalid id should error' do
|
161
|
-
|
162
|
-
id 'does not exist'
|
163
|
-
rescue Exception => e
|
164
|
-
message = e.message
|
165
|
-
end
|
166
|
-
message.must_equal 'Invalid id `does not exist`'
|
162
|
+
proc { id 'does not exist' }.must_raise Selenium::WebDriver::Error::NoSuchElementError
|
167
163
|
|
168
164
|
# resource id should error on ios
|
169
|
-
|
170
|
-
id 'android:id/text1'
|
171
|
-
rescue Exception => e
|
172
|
-
message = e.message
|
173
|
-
end
|
174
|
-
message.must_equal 'Invalid id `android:id/text1`'
|
165
|
+
proc { id 'android:id/text1' }.must_raise Selenium::WebDriver::Error::NoSuchElementError
|
175
166
|
end
|
176
167
|
|
177
168
|
t 'tag' do
|
178
|
-
tag('UIATableCell').name.must_equal
|
169
|
+
tag('UIATableCell').name.must_equal uibutton_text
|
179
170
|
end
|
180
171
|
|
181
172
|
t 'tags' do
|
@@ -183,7 +174,7 @@ must_not_raise is a no-op.
|
|
183
174
|
end
|
184
175
|
|
185
176
|
t 'find_eles_by_attr_include' do
|
186
|
-
find_eles_by_attr_include('
|
177
|
+
find_eles_by_attr_include('UIAStaticText', 'name', 'Use').length.must_equal 7
|
187
178
|
end
|
188
179
|
|
189
180
|
t 'get_page_class' do
|
@@ -209,4 +200,4 @@ xpath_visible_exact
|
|
209
200
|
xpaths_visible_exact
|
210
201
|
raise_no_element_error
|
211
202
|
=end
|
212
|
-
end
|
203
|
+
end
|
@@ -2,10 +2,15 @@
|
|
2
2
|
describe 'the web context' do
|
3
3
|
|
4
4
|
t 'get_android_inspect' do
|
5
|
-
text('Web
|
5
|
+
text('Web').click
|
6
6
|
set_context 'WEBVIEW'
|
7
7
|
current_context.must_equal 'WEBVIEW_1'
|
8
8
|
sleep 1 #Give a chance to load
|
9
9
|
page.start_with?("\nhtml\n").must_equal true
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
|
+
t 'after_last' do
|
13
|
+
set_context 'NATIVE_APP'
|
14
|
+
back_click
|
15
|
+
end
|
16
|
+
end
|
@@ -3,6 +3,12 @@ describe 'device/device' do
|
|
3
3
|
screen.must_equal catalog
|
4
4
|
end
|
5
5
|
|
6
|
+
# go back to the main page
|
7
|
+
def go_back
|
8
|
+
back
|
9
|
+
wait { ! exists { id 'ArrowButton' } } # successfully transitioned back
|
10
|
+
end
|
11
|
+
|
6
12
|
t 'before_first' do
|
7
13
|
before_first
|
8
14
|
end
|
@@ -13,7 +19,7 @@ describe 'device/device' do
|
|
13
19
|
|
14
20
|
# It appears that lockForDuration doesn't.
|
15
21
|
close_app
|
16
|
-
|
22
|
+
launch_app
|
17
23
|
end
|
18
24
|
|
19
25
|
t 'background_app' do
|
@@ -32,7 +38,7 @@ describe 'device/device' do
|
|
32
38
|
|
33
39
|
t 'close and launch' do
|
34
40
|
close_app
|
35
|
-
|
41
|
+
launch_app
|
36
42
|
tag('UIANavigationBar').name.must_equal 'UICatalog'
|
37
43
|
end
|
38
44
|
|
@@ -45,12 +51,12 @@ describe 'device/device' do
|
|
45
51
|
end
|
46
52
|
|
47
53
|
t 'current_context' do
|
48
|
-
current_context.must_equal
|
54
|
+
current_context.must_equal 'NATIVE_APP'
|
49
55
|
end
|
50
56
|
|
51
57
|
t 'switch_to_default_context' do
|
52
58
|
switch_to_default_context
|
53
|
-
current_context.must_equal
|
59
|
+
current_context.must_equal 'NATIVE_APP'
|
54
60
|
end
|
55
61
|
|
56
62
|
t 'app_strings' do
|
@@ -59,11 +65,9 @@ describe 'device/device' do
|
|
59
65
|
end
|
60
66
|
|
61
67
|
t 'action_chain' do
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
ac.perform
|
66
|
-
back
|
68
|
+
Appium::TouchAction.new.press(element: id('ButtonsExplain')).perform
|
69
|
+
wait { id 'ArrowButton' } # successfully transitioned to buttons page
|
70
|
+
go_back
|
67
71
|
end
|
68
72
|
|
69
73
|
t 'swipe' do
|
@@ -71,9 +75,11 @@ describe 'device/device' do
|
|
71
75
|
end
|
72
76
|
|
73
77
|
t 'pinch & zoom' do
|
74
|
-
|
78
|
+
wait { id('ImagesExplain').click }
|
79
|
+
# both of these appear to do nothing on iOS 8
|
75
80
|
zoom 200
|
76
81
|
pinch 75
|
82
|
+
go_back
|
77
83
|
end
|
78
84
|
|
79
85
|
t 'pull_file' do
|
@@ -36,6 +36,7 @@ describe 'driver' do
|
|
36
36
|
actual = driver_attributes
|
37
37
|
actual[:caps][:app] = File.basename actual[:caps][:app]
|
38
38
|
expected = { caps: { platformName: 'ios',
|
39
|
+
platformVersion: '8.1',
|
39
40
|
deviceName: 'iPhone Simulator',
|
40
41
|
app: 'UICatalog.app' },
|
41
42
|
custom_url: false,
|
@@ -195,7 +196,7 @@ describe 'driver' do
|
|
195
196
|
|
196
197
|
# any elements
|
197
198
|
t 'find_elements' do
|
198
|
-
find_elements(:class, '
|
199
|
+
find_elements(:class, 'UIATableCell').length.must_equal 12
|
199
200
|
end
|
200
201
|
|
201
202
|
# any element
|
@@ -203,6 +204,16 @@ describe 'driver' do
|
|
203
204
|
find_element(:class, 'UIAStaticText').class.must_equal Selenium::WebDriver::Element
|
204
205
|
end
|
205
206
|
|
207
|
+
# settings
|
208
|
+
t 'get settings' do
|
209
|
+
get_settings.wont_be_nil
|
210
|
+
end
|
211
|
+
|
212
|
+
t 'update settings' do
|
213
|
+
update_settings cyberdelia: 'open'
|
214
|
+
get_settings['cyberdelia'].must_equal 'open'
|
215
|
+
end
|
216
|
+
|
206
217
|
# Skip: x # x is only used in Pry
|
207
218
|
end
|
208
|
-
end
|
219
|
+
end
|
@@ -21,16 +21,17 @@ describe 'ios/element/button' do
|
|
21
21
|
|
22
22
|
t 'button' do
|
23
23
|
# by index
|
24
|
-
button(
|
24
|
+
button(3).name.must_equal gray
|
25
25
|
|
26
26
|
# by name contains
|
27
27
|
button('ray').name.must_equal gray
|
28
28
|
end
|
29
29
|
|
30
30
|
t 'buttons' do
|
31
|
-
exp = ['Back', 'Gray', 'Right pointing arrow']
|
32
|
-
buttons('a')
|
33
|
-
|
31
|
+
exp = ['Back', 'Back', 'Gray', 'Right pointing arrow']
|
32
|
+
target_buttons = buttons('a')
|
33
|
+
target_buttons.map { |e| e.name }.must_equal exp
|
34
|
+
target_buttons.length.must_equal exp.length
|
34
35
|
end
|
35
36
|
|
36
37
|
t 'first_button' do
|
@@ -5,7 +5,7 @@ describe 'ios/element/generic' do
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def uibutton_text
|
8
|
-
'Buttons
|
8
|
+
'Buttons'
|
9
9
|
end
|
10
10
|
|
11
11
|
def verify element
|
@@ -32,4 +32,4 @@ describe 'ios/element/generic' do
|
|
32
32
|
t 'finds_exact' do
|
33
33
|
verify finds_exact uibutton_text
|
34
34
|
end
|
35
|
-
end
|
35
|
+
end
|
@@ -4,6 +4,10 @@ describe 'ios/element/text' do
|
|
4
4
|
'UICatalog'
|
5
5
|
end
|
6
6
|
|
7
|
+
def uiview_transitions
|
8
|
+
'Transitions'
|
9
|
+
end
|
10
|
+
|
7
11
|
def before_first
|
8
12
|
screen.must_equal catalog
|
9
13
|
end
|
@@ -17,19 +21,19 @@ describe 'ios/element/text' do
|
|
17
21
|
end
|
18
22
|
|
19
23
|
t 'last_text' do
|
20
|
-
last_text.text.must_equal
|
21
|
-
last_text.name.must_equal
|
24
|
+
last_text.text.must_equal uiview_transitions
|
25
|
+
last_text.name.must_equal uiview_transitions
|
22
26
|
end
|
23
27
|
|
24
28
|
t 'text' do
|
25
|
-
text('
|
29
|
+
text('siti').text.must_equal uiview_transitions
|
26
30
|
text(1).text.must_equal ui_catalog
|
27
|
-
text('
|
31
|
+
text('siti').name.must_equal uiview_transitions
|
28
32
|
end
|
29
33
|
|
30
34
|
t 'texts' do
|
31
|
-
exp = ['Controls, Various uses of UIControl', '
|
32
|
-
texts.length.must_equal
|
35
|
+
exp = ['Controls', 'Various uses of UIControl', 'Various uses of UISegmentedControl']
|
36
|
+
texts.length.must_equal 24
|
33
37
|
texts('trol').map { |e| e.name }.must_equal exp
|
34
38
|
texts('uses').length.must_equal 7
|
35
39
|
end
|
@@ -51,4 +55,4 @@ describe 'ios/element/text' do
|
|
51
55
|
t 'texts_exact' do
|
52
56
|
texts_exact('UICatalog').length.must_equal 1
|
53
57
|
end
|
54
|
-
end
|
58
|
+
end
|
@@ -23,7 +23,7 @@ describe 'ios/element/textfield' do
|
|
23
23
|
t 'textfield' do
|
24
24
|
textfield(1).text.must_equal(enter_text)
|
25
25
|
textfield(enter_text).text.must_equal(enter_text)
|
26
|
-
textfield('word').
|
26
|
+
textfield('word').value.must_equal enter_password
|
27
27
|
end
|
28
28
|
|
29
29
|
t 'textfields' do
|
@@ -43,15 +43,15 @@ describe 'ios/element/textfield' do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
t 'last_textfield' do
|
46
|
-
last_textfield.text.must_equal
|
46
|
+
last_textfield.text.must_equal enter_text
|
47
47
|
end
|
48
48
|
|
49
49
|
t 'textfield_exact' do
|
50
|
-
textfield_exact(enter_password).
|
50
|
+
textfield_exact(enter_password).value.must_equal enter_password
|
51
51
|
end
|
52
52
|
|
53
53
|
t 'textfields_exact' do
|
54
|
-
textfields_exact(enter_password).first.
|
54
|
+
textfields_exact(enter_password).first.value.must_equal enter_password
|
55
55
|
end
|
56
56
|
|
57
57
|
def keyboard_exists?
|
@@ -83,6 +83,11 @@ describe 'ios/element/textfield' do
|
|
83
83
|
proc { block.call }.must_raise Selenium::WebDriver::Error::NoSuchElementError
|
84
84
|
end
|
85
85
|
|
86
|
+
t 'hide_keyboard' do
|
87
|
+
first_textfield.click
|
88
|
+
hide_keyboard
|
89
|
+
end
|
90
|
+
|
86
91
|
# test textfield methods with no textfields
|
87
92
|
|
88
93
|
t 'leave textfields' do
|
@@ -115,11 +120,6 @@ describe 'ios/element/textfield' do
|
|
115
120
|
textfields_exact('does not exist').length.must_equal 0
|
116
121
|
end
|
117
122
|
|
118
|
-
t 'hide_keyboard' do
|
119
|
-
first_textfield.click
|
120
|
-
hide_keyboard
|
121
|
-
end
|
122
|
-
|
123
123
|
t 'after_last' do
|
124
124
|
after_last
|
125
125
|
end
|
data/lib/appium_lib.rb
CHANGED
@@ -5,24 +5,8 @@ Encoding.default_internal = Encoding::UTF_8
|
|
5
5
|
require 'forwardable' unless defined? Forwardable
|
6
6
|
require_relative 'appium_lib/rails/duplicable'
|
7
7
|
|
8
|
+
# Init global driver
|
8
9
|
$driver = nil
|
9
10
|
|
10
|
-
# @private
|
11
|
-
# Invoke top level methods on last created Appium driver.
|
12
|
-
def self.method_missing method, *args, &block
|
13
|
-
raise "driver is nil. called #{method}" if $driver == nil
|
14
|
-
|
15
|
-
if $driver.respond_to?(method)
|
16
|
-
# puts "[method_missing] Calling driver.send for #{method}"
|
17
|
-
$driver.send(method, *args, &block)
|
18
|
-
elsif self.respond_to?(method)
|
19
|
-
# puts "[method_missing] Calling super with args for #{method}"
|
20
|
-
super(*args, &block)
|
21
|
-
else
|
22
|
-
# puts "[method_missing] Calling super (no args) for #{method}"
|
23
|
-
super
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
11
|
require_relative 'appium_lib/logger'
|
28
12
|
require_relative 'appium_lib/driver'
|
@@ -18,13 +18,13 @@ module Appium
|
|
18
18
|
|
19
19
|
def initialize
|
20
20
|
reset
|
21
|
-
@filter
|
21
|
+
@filter = false
|
22
22
|
@instance = Hash.new -1
|
23
23
|
end
|
24
24
|
|
25
25
|
def reset
|
26
|
-
@result
|
27
|
-
@keys
|
26
|
+
@result = ''
|
27
|
+
@keys = %w[text resource-id content-desc]
|
28
28
|
@instance = Hash.new -1
|
29
29
|
end
|
30
30
|
|
@@ -83,6 +83,41 @@ module Appium
|
|
83
83
|
end
|
84
84
|
end # class AndroidElements
|
85
85
|
|
86
|
+
# Fix uiautomator's xml dump.
|
87
|
+
# https://github.com/appium/appium/issues/2822
|
88
|
+
# https://code.google.com/p/android/issues/detail?id=74143
|
89
|
+
def _fix_android_native_source source
|
90
|
+
# <android.app.ActionBar$Tab
|
91
|
+
# <android.app.ActionBar $ Tab
|
92
|
+
|
93
|
+
# find each closing tag that contains a dollar sign.
|
94
|
+
source.scan(/<\/([^>]*\$[^>]*)>/).flatten.uniq.each do |problem_tag|
|
95
|
+
# "android.app.ActionBar$Tab"
|
96
|
+
before, after = problem_tag.split('$')
|
97
|
+
before.strip!
|
98
|
+
after.strip!
|
99
|
+
|
100
|
+
fixed = "#{before}.#{after}"
|
101
|
+
|
102
|
+
# now escape . in before/after because they're used in regex
|
103
|
+
before.gsub!('.', '\.')
|
104
|
+
after.gsub!('.', '\.')
|
105
|
+
|
106
|
+
# <android.app.ActionBar$Tab => <android.app.ActionBar.Tab
|
107
|
+
# </android.app.ActionBar$Tab> => </android.app.ActionBar.Tab>
|
108
|
+
source = source.gsub(/<#{before}\s*\$\s*#{after}/, "<#{fixed}").
|
109
|
+
gsub(/<\/#{before}\s*\$\s*#{after}>/, "</#{fixed}>")
|
110
|
+
end
|
111
|
+
|
112
|
+
source
|
113
|
+
end
|
114
|
+
|
115
|
+
# Prints xml of the current page
|
116
|
+
# @return [void]
|
117
|
+
def source
|
118
|
+
_print_source get_source
|
119
|
+
end
|
120
|
+
|
86
121
|
# Android only.
|
87
122
|
# Returns a string containing interesting elements.
|
88
123
|
# The text, content description, and id are returned.
|
@@ -91,10 +126,15 @@ module Appium
|
|
91
126
|
# @return [String]
|
92
127
|
def get_android_inspect class_name=false
|
93
128
|
source = get_source
|
94
|
-
|
129
|
+
|
130
|
+
doctype_string = '<!doctyp'
|
131
|
+
source_header = source[0..doctype_string.length].downcase
|
132
|
+
source_is_html = source_header.start_with?(doctype_string, '<html')
|
133
|
+
|
134
|
+
if source_is_html # parse html from webview
|
95
135
|
parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
|
96
136
|
else
|
97
|
-
parser = @
|
137
|
+
parser = @android_native_parser ||= Nokogiri::XML::SAX::Parser.new(AndroidElements.new)
|
98
138
|
end
|
99
139
|
parser.document.reset # ensure document is reset before parsing
|
100
140
|
parser.document.filter = class_name
|
@@ -283,5 +323,15 @@ module Appium
|
|
283
323
|
def complex_finds_exact class_name, value
|
284
324
|
find_elements :uiautomator, string_visible_exact(class_name, value)
|
285
325
|
end
|
326
|
+
|
327
|
+
# Returns XML string for the current page
|
328
|
+
# Fixes uiautomator's $ in node names.
|
329
|
+
# `android.app.ActionBar$Tab` becomes `android.app.ActionBar.Tab`
|
330
|
+
# @return [String]
|
331
|
+
def get_source
|
332
|
+
src = @driver.page_source
|
333
|
+
src = _fix_android_native_source src unless src && src.start_with?('<html>')
|
334
|
+
src
|
335
|
+
end
|
286
336
|
end # module Android
|
287
337
|
end # module Appium
|