appium_lib 1.0.0 → 2.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/Rakefile +48 -14
- data/android_tests/Rakefile +5 -2
- data/android_tests/api.apk +0 -0
- data/android_tests/lib/android/specs/android/element/alert.rb +1 -1
- data/android_tests/lib/android/specs/android/element/button.rb +1 -4
- data/android_tests/lib/android/specs/android/element/text.rb +15 -18
- data/android_tests/lib/android/specs/android/element/textfield.rb +1 -4
- data/android_tests/lib/android/specs/android/helper.rb +4 -6
- data/android_tests/lib/android/specs/common/device.rb +16 -6
- data/android_tests/lib/android/specs/common/helper.rb +6 -6
- data/android_tests/lib/android/specs/common/patch.rb +3 -3
- data/android_tests/lib/android/specs/driver.rb +1 -1
- data/appium_lib.gemspec +11 -11
- data/docs/android_docs.md +193 -210
- data/docs/ios_docs.md +505 -177
- data/docs/migration.md +27 -0
- data/docs_gen/make_docs.rb +17 -17
- data/ios_tests/Rakefile +6 -3
- data/ios_tests/lib/ios/specs/common/helper.rb +1 -1
- data/ios_tests/lib/ios/specs/common/patch.rb +2 -2
- data/ios_tests/lib/ios/specs/device/device.rb +3 -2
- data/ios_tests/lib/ios/specs/device/multi_touch.rb +1 -1
- data/ios_tests/lib/ios/specs/device/touch_actions.rb +2 -2
- data/ios_tests/lib/ios/specs/driver.rb +5 -5
- data/ios_tests/lib/ios/specs/ios/element/alert.rb +5 -5
- data/ios_tests/lib/ios/specs/ios/element/button.rb +2 -5
- data/ios_tests/lib/ios/specs/ios/element/text.rb +21 -21
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +1 -8
- data/ios_tests/lib/ios/specs/ios/helper.rb +2 -2
- data/ios_tests/lib/ios/specs/ios/patch.rb +1 -1
- data/ios_tests/lib/run.rb +1 -1
- data/ios_tests/upload/sauce_storage.rb +13 -12
- data/ios_tests/upload/upload.rb +1 -1
- data/lib/appium_lib/android/dynamic.rb +31 -30
- data/lib/appium_lib/android/element/button.rb +7 -11
- data/lib/appium_lib/android/element/generic.rb +5 -5
- data/lib/appium_lib/android/element/text.rb +8 -12
- data/lib/appium_lib/android/element/textfield.rb +3 -7
- data/lib/appium_lib/android/helper.rb +46 -10
- data/lib/appium_lib/common/helper.rb +4 -2
- data/lib/appium_lib/common/patch.rb +1 -1
- data/lib/appium_lib/common/version.rb +2 -2
- data/lib/appium_lib/device/device.rb +34 -27
- data/lib/appium_lib/device/multi_touch.rb +1 -1
- data/lib/appium_lib/device/touch_actions.rb +30 -28
- data/lib/appium_lib/driver.rb +3 -3
- data/lib/appium_lib/ios/element/button.rb +4 -8
- data/lib/appium_lib/ios/element/text.rb +8 -12
- data/lib/appium_lib/ios/element/textfield.rb +3 -7
- data/lib/appium_lib/ios/helper.rb +70 -40
- data/readme.md +6 -70
- data/release_notes.md +26 -0
- metadata +3 -2
data/docs/migration.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
### Breaking Changes in 2.0
|
2
|
+
|
3
|
+
In 2.0, the e_* methods have been renamed.
|
4
|
+
|
5
|
+
Old | New
|
6
|
+
:--|:--
|
7
|
+
`e_buttons` | `buttons`
|
8
|
+
`e_s_texts` | `texts`
|
9
|
+
`e_textfields` | `textfields`
|
10
|
+
|
11
|
+
All s_texts methods have been renamed.
|
12
|
+
|
13
|
+
Old | New
|
14
|
+
:--|:--
|
15
|
+
`s_text` | `text`
|
16
|
+
`s_texts` | `texts`
|
17
|
+
`first_s_text` | `first_text`
|
18
|
+
`last_s_text` | `last_text`
|
19
|
+
`s_text_exact` | `text_exact`
|
20
|
+
`s_texts_exact` | `texts_exact`
|
21
|
+
|
22
|
+
Other changes:
|
23
|
+
|
24
|
+
Old | New
|
25
|
+
:--|:--
|
26
|
+
`press_for_duration` | `long_press`
|
27
|
+
`current_context=` | `set_context`
|
data/docs_gen/make_docs.rb
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'yard'
|
3
3
|
|
4
4
|
def space amount
|
5
|
-
|
5
|
+
' ' * amount
|
6
6
|
end
|
7
7
|
|
8
8
|
def last_sha
|
@@ -17,18 +17,18 @@ def mobj_to_md obj
|
|
17
17
|
|
18
18
|
# skip class vars
|
19
19
|
if sig.start_with?('@@') ||
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
# skip methods marked private
|
21
|
+
obj.tag('private') ||
|
22
|
+
# skip date and version from version.rb
|
23
|
+
obj.name.match(/DATE|VERSION/)
|
24
|
+
return out
|
25
25
|
end
|
26
26
|
|
27
27
|
method_path = obj.file.split('/lib/').last
|
28
|
-
os
|
29
|
-
out
|
30
|
-
out
|
31
|
-
out
|
28
|
+
os = method_path.downcase.match /ios|android/
|
29
|
+
out += "##### [#{obj.name.to_s}](https://github.com/appium/ruby_lib/blob/#{last_sha}/lib/#{method_path}#L#{obj.line}) #{os}\n\n"
|
30
|
+
out += "> #{obj.signature}\n\n"
|
31
|
+
out += "#{obj.docstring}\n\n"
|
32
32
|
|
33
33
|
|
34
34
|
indent = space 5
|
@@ -56,12 +56,12 @@ def run out_file, globs
|
|
56
56
|
puts "Globbing: #{globs}"
|
57
57
|
puts "Writing: #{out_file}"
|
58
58
|
YARD::Parser::SourceParser.parse globs
|
59
|
-
File.open(out_file, 'w') do |
|
60
|
-
entries
|
59
|
+
File.open(out_file, 'w') do |file|
|
60
|
+
entries = YARD::Registry.entries
|
61
61
|
entries_length = entries.length
|
62
62
|
puts "Processing: #{entries_length} entries"
|
63
63
|
raise 'No entries to process' if entries_length <= 0
|
64
|
-
YARD::Registry.entries.each do |
|
64
|
+
YARD::Registry.entries.each do |entry|
|
65
65
|
file.write mobj_to_md entry
|
66
66
|
end
|
67
67
|
end
|
@@ -71,14 +71,14 @@ end
|
|
71
71
|
|
72
72
|
def globs paths
|
73
73
|
# Convert single string to array for map
|
74
|
-
paths = [
|
74
|
+
paths = [paths] unless paths.kind_of? Array
|
75
75
|
# Adjust path based on system
|
76
76
|
paths.map! { |path| "#{Dir.pwd}#{path}" }
|
77
77
|
end
|
78
78
|
|
79
|
-
common_globs
|
80
|
-
android_globs = common_globs + [
|
81
|
-
ios_globs
|
79
|
+
common_globs = '/lib/appium_lib/*.rb', '/lib/appium_lib/device/*.rb', '/lib/appium_lib/common/**/*.rb'
|
80
|
+
android_globs = common_globs + ['/lib/appium_lib/android/**/*.rb']
|
81
|
+
ios_globs = common_globs + ['/lib/appium_lib/ios/**/*.rb']
|
82
82
|
|
83
83
|
run 'docs/android_docs.md', globs(android_globs)
|
84
84
|
|
data/ios_tests/Rakefile
CHANGED
@@ -5,7 +5,10 @@ task :default => :ios
|
|
5
5
|
|
6
6
|
# Run sh and ignore exception
|
7
7
|
def run_sh cmd
|
8
|
-
begin
|
8
|
+
begin
|
9
|
+
sh cmd
|
10
|
+
rescue
|
11
|
+
end
|
9
12
|
end
|
10
13
|
|
11
14
|
# Run cmd. On failure run install and try again.
|
@@ -32,8 +35,8 @@ task :ios, :args, :test_file do |args, test_file|
|
|
32
35
|
# rake android['ok']
|
33
36
|
# args = android
|
34
37
|
# test_file = {:args=>"ok"}
|
35
|
-
test_file
|
36
|
-
path
|
38
|
+
test_file = test_file[:args]
|
39
|
+
path = File.expand_path('appium.txt', Rake.application.original_dir)
|
37
40
|
ENV['APPIUM_TXT'] = path
|
38
41
|
puts "Rake appium.txt path is: #{path}"
|
39
42
|
cmd = 'bundle exec ruby ./lib/run.rb ios'
|
@@ -65,7 +65,7 @@ must_not_raise is a no-op.
|
|
65
65
|
# start page
|
66
66
|
tag('UIANavigationBar').name.must_equal 'UICatalog'
|
67
67
|
# nav to new page.
|
68
|
-
wait_true {
|
68
|
+
wait_true { text('buttons').click; tag('UIANavigationBar').name == 'Buttons' }
|
69
69
|
tag('UIANavigationBar').name.must_equal 'Buttons'
|
70
70
|
# go back
|
71
71
|
back_click
|
@@ -19,13 +19,13 @@ describe 'common/patch.rb' do
|
|
19
19
|
# todo: t 'value' do; end
|
20
20
|
|
21
21
|
t 'name' do
|
22
|
-
|
22
|
+
first_text.name.must_equal 'UICatalog'
|
23
23
|
end
|
24
24
|
|
25
25
|
# t 'tag_name' do; end # Doesn't work on Android
|
26
26
|
|
27
27
|
t 'location_rel' do
|
28
|
-
loc =
|
28
|
+
loc = first_text.location_rel
|
29
29
|
loc.x.class.must_equal String
|
30
30
|
loc.y.class.must_equal String
|
31
31
|
end
|
@@ -55,6 +55,7 @@ describe 'device/device' do
|
|
55
55
|
|
56
56
|
t 'app_strings' do
|
57
57
|
app_strings.must_include "SearchBarExplain"
|
58
|
+
app_strings('en').must_include "SearchBarExplain"
|
58
59
|
end
|
59
60
|
|
60
61
|
t 'action_chain' do
|
@@ -66,11 +67,11 @@ describe 'device/device' do
|
|
66
67
|
end
|
67
68
|
|
68
69
|
t 'swipe' do
|
69
|
-
swipe start_x: 75, start_y: 500, end_x: 75, end_y: 0, duration:
|
70
|
+
swipe start_x: 75, start_y: 500, end_x: 75, end_y: 0, duration: 800
|
70
71
|
end
|
71
72
|
|
72
73
|
t 'pinch & zoom' do
|
73
|
-
|
74
|
+
text('Images, Use of UIImageView').click
|
74
75
|
zoom 200
|
75
76
|
pinch 75
|
76
77
|
end
|
@@ -44,8 +44,8 @@ describe 'driver' do
|
|
44
44
|
debug: true }
|
45
45
|
|
46
46
|
if actual != expected
|
47
|
-
diff
|
48
|
-
diff
|
47
|
+
diff = HashDiff.diff expected, actual
|
48
|
+
diff = "diff (expected, actual):\n#{diff}"
|
49
49
|
# example:
|
50
50
|
# change :ios in expected to match 'ios' in actual
|
51
51
|
# [["~", "caps.platformName", :ios, "ios"]]
|
@@ -56,8 +56,8 @@ describe 'driver' do
|
|
56
56
|
|
57
57
|
t 'verify attributes are immutable' do
|
58
58
|
driver_attributes[:caps][:app] = 'fake'
|
59
|
-
actual
|
60
|
-
expected
|
59
|
+
actual = File.basename driver_attributes[:caps][:app]
|
60
|
+
expected = 'UICatalog.app'
|
61
61
|
actual.must_equal expected
|
62
62
|
end
|
63
63
|
|
@@ -139,7 +139,7 @@ describe 'driver' do
|
|
139
139
|
|
140
140
|
t 'restart' do
|
141
141
|
restart
|
142
|
-
|
142
|
+
text 'buttons'
|
143
143
|
end
|
144
144
|
|
145
145
|
t 'driver' do
|
@@ -2,7 +2,7 @@
|
|
2
2
|
describe 'ios/element/alert' do
|
3
3
|
def nav_once
|
4
4
|
screen.must_equal catalog
|
5
|
-
wait_true {
|
5
|
+
wait_true { text('alerts').click; tag('UIANavigationBar').name == 'Alerts' } # wait for true
|
6
6
|
tag('UIANavigationBar').name.must_equal 'Alerts'
|
7
7
|
|
8
8
|
# redefine method as no-op after it's invoked once
|
@@ -10,7 +10,7 @@ describe 'ios/element/alert' do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def after_last
|
13
|
-
alert_accept if exists {
|
13
|
+
alert_accept if exists { text('UIActionSheet <title>') }
|
14
14
|
back_click
|
15
15
|
screen.must_equal catalog
|
16
16
|
sleep 1
|
@@ -23,9 +23,9 @@ describe 'ios/element/alert' do
|
|
23
23
|
|
24
24
|
def open_alert
|
25
25
|
wait_true do
|
26
|
-
return true if exists {
|
27
|
-
|
28
|
-
|
26
|
+
return true if exists { text('UIActionSheet <title>') }
|
27
|
+
text('Show OK-Cancel').click
|
28
|
+
text('UIActionSheet <title>').displayed?
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -3,7 +3,7 @@ describe 'ios/element/button' do
|
|
3
3
|
def before_first
|
4
4
|
screen.must_equal catalog
|
5
5
|
# nav to buttons activity
|
6
|
-
wait {
|
6
|
+
wait { text('buttons').click }
|
7
7
|
end
|
8
8
|
|
9
9
|
def after_last
|
@@ -30,6 +30,7 @@ describe 'ios/element/button' do
|
|
30
30
|
t 'buttons' do
|
31
31
|
exp = ['Back', 'Gray', 'Right pointing arrow']
|
32
32
|
buttons('a').map { |e| e.name }.must_equal exp
|
33
|
+
buttons.length.must_equal 4
|
33
34
|
end
|
34
35
|
|
35
36
|
t 'first_button' do
|
@@ -48,10 +49,6 @@ describe 'ios/element/button' do
|
|
48
49
|
buttons_exact(gray).first.name.must_equal gray
|
49
50
|
end
|
50
51
|
|
51
|
-
t 'e_buttons' do
|
52
|
-
e_buttons.length.must_equal 4
|
53
|
-
end
|
54
|
-
|
55
52
|
t 'after_last' do
|
56
53
|
after_last
|
57
54
|
end
|
@@ -12,43 +12,43 @@ describe 'ios/element/text' do
|
|
12
12
|
before_first
|
13
13
|
end
|
14
14
|
|
15
|
-
t '
|
16
|
-
|
15
|
+
t 'first_text' do
|
16
|
+
first_text.text.must_equal ui_catalog
|
17
17
|
end
|
18
18
|
|
19
|
-
t '
|
20
|
-
|
19
|
+
t 'last_text' do
|
20
|
+
last_text.text.must_equal ''
|
21
|
+
last_text.name.must_equal 'Transitions, Shows UIViewAnimationTransitions'
|
21
22
|
end
|
22
23
|
|
23
|
-
t '
|
24
|
-
|
25
|
-
|
24
|
+
t 'text' do
|
25
|
+
text('mat').text.must_equal ''
|
26
|
+
text(1).text.must_equal ui_catalog
|
27
|
+
text('mat').name.must_equal 'Transitions, Shows UIViewAnimationTransitions'
|
26
28
|
end
|
27
29
|
|
28
|
-
t '
|
29
|
-
s_text('mat').text.must_equal ''
|
30
|
-
s_text(1).text.must_equal ui_catalog
|
31
|
-
s_text('mat').name.must_equal 'Transitions, Shows UIViewAnimationTransitions'
|
32
|
-
end
|
33
|
-
|
34
|
-
t 's_texts' do
|
30
|
+
t 'texts' do
|
35
31
|
exp = ['Controls, Various uses of UIControl', 'Segments, Various uses of UISegmentedControl']
|
36
|
-
|
37
|
-
|
32
|
+
texts.length.must_equal 13
|
33
|
+
texts('trol').map { |e| e.name }.must_equal exp
|
34
|
+
texts('uses').length.must_equal 7
|
38
35
|
end
|
39
36
|
|
40
|
-
t '
|
37
|
+
t 'text_exact' do
|
41
38
|
# should fail
|
42
39
|
set_wait 0
|
43
|
-
act = begin
|
40
|
+
act = begin
|
41
|
+
text_exact 'mos'
|
42
|
+
rescue
|
43
|
+
end
|
44
44
|
act.must_be_nil
|
45
45
|
set_wait
|
46
46
|
|
47
47
|
# should pass
|
48
|
-
|
48
|
+
text_exact(ui_catalog).text.must_equal ui_catalog
|
49
49
|
end
|
50
50
|
|
51
|
-
t '
|
52
|
-
|
51
|
+
t 'texts_exact' do
|
52
|
+
texts_exact('UICatalog').length.must_equal 1
|
53
53
|
end
|
54
54
|
end
|
@@ -30,6 +30,7 @@ describe 'ios/element/textfield' do
|
|
30
30
|
values = textfields('enter').map { |e| e.value }
|
31
31
|
values.include?('<enter text>').must_equal true
|
32
32
|
values.include?('<enter password>').must_equal true
|
33
|
+
textfields.length.must_equal 4
|
33
34
|
end
|
34
35
|
|
35
36
|
t 'first_textfield' do
|
@@ -48,10 +49,6 @@ describe 'ios/element/textfield' do
|
|
48
49
|
textfields_exact(enter_password).first.text.must_equal enter_password
|
49
50
|
end
|
50
51
|
|
51
|
-
t 'e_textfields' do
|
52
|
-
e_textfields.length.must_equal 4
|
53
|
-
end
|
54
|
-
|
55
52
|
def keyboard_exists?
|
56
53
|
!!ignore { wait_true(3) { execute_script 'au.mainApp().keyboard().type() !== "UIAElementNil"' } }
|
57
54
|
end
|
@@ -113,10 +110,6 @@ describe 'ios/element/textfield' do
|
|
113
110
|
textfields_exact('does not exist').length.must_equal 0
|
114
111
|
end
|
115
112
|
|
116
|
-
t 'no e_textfields' do
|
117
|
-
e_textfields.length.must_equal 0
|
118
|
-
end
|
119
|
-
|
120
113
|
t 'after_last' do
|
121
114
|
after_last
|
122
115
|
end
|
@@ -9,8 +9,8 @@ describe 'ios/helper' do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
t 'ios_password' do
|
12
|
-
ios_password.must_equal '
|
13
|
-
ios_password(2).must_equal '
|
12
|
+
ios_password.must_equal 8226.chr('UTF-8')
|
13
|
+
ios_password(2).must_equal 8226.chr('UTF-8') * 2
|
14
14
|
end
|
15
15
|
|
16
16
|
# todo: t 'get_page' do
|
data/ios_tests/lib/run.rb
CHANGED
@@ -34,7 +34,7 @@ end
|
|
34
34
|
|
35
35
|
def go_to_textfields
|
36
36
|
screen.must_equal catalog
|
37
|
-
wait_true {
|
37
|
+
wait_true { text('textfield').click; screen == 'TextFields' } # wait for screen transition
|
38
38
|
screen.must_equal 'TextFields'
|
39
39
|
end
|
40
40
|
|
@@ -26,30 +26,31 @@ remote_md5: 764efa883dda1e11db47671c4a3bbd9e
|
|
26
26
|
require 'rubygems'
|
27
27
|
require 'restclient'
|
28
28
|
require 'json'
|
29
|
-
|
29
|
+
|
30
30
|
class SauceStorage
|
31
31
|
attr_reader :username, :key, :url, :debug
|
32
|
+
|
32
33
|
def initialize opts
|
33
34
|
@username = opts.fetch :username, ENV['SAUCE_USERNAME']
|
34
|
-
@key
|
35
|
-
@url
|
36
|
-
@debug
|
35
|
+
@key = opts.fetch :key, ENV['SAUCE_ACCESS_KEY']
|
36
|
+
@url = "https://#{@username}:#{@key}@saucelabs.com/rest/v1/storage/#{@username}"
|
37
|
+
@debug = opts.fetch :debug, false
|
37
38
|
end
|
38
|
-
|
39
|
+
|
39
40
|
def upload file_path
|
40
41
|
file_name = File.basename file_path
|
41
|
-
file
|
42
|
+
file = File.new file_path
|
42
43
|
local_md5 = Digest::MD5.hexdigest File.read file_path
|
43
|
-
|
44
|
+
|
44
45
|
self.files.each do |file|
|
45
46
|
if file['md5'] == local_md5
|
46
47
|
puts 'File already uploaded' if @debug
|
47
48
|
return true
|
48
49
|
end
|
49
50
|
end
|
50
|
-
|
51
|
-
url
|
52
|
-
remote_md5 = JSON.parse(
|
51
|
+
|
52
|
+
url = "#{@url}/#{file_name}?overwrite=plz"
|
53
|
+
remote_md5 = JSON.parse(RestClient.post url, file, content_type: 'application/octet-stream')['md5']
|
53
54
|
if @debug
|
54
55
|
puts "Uploaded #{file_path}"
|
55
56
|
puts " local_md5: #{local_md5}"
|
@@ -57,8 +58,8 @@ class SauceStorage
|
|
57
58
|
end
|
58
59
|
local_md5 == remote_md5
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
def files
|
62
|
-
JSON.parse(
|
63
|
+
JSON.parse(RestClient.get @url)['files']
|
63
64
|
end
|
64
65
|
end
|