appium_lib 6.0.0 → 7.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/.gitignore +2 -0
- data/.rubocop.yml +28 -0
- data/.travis.yml +10 -0
- data/Rakefile +9 -1
- data/android_tests/Gemfile +1 -1
- data/android_tests/Rakefile +20 -13
- data/android_tests/lib/android/specs/android/element/alert.rb +1 -1
- data/android_tests/lib/android/specs/android/element/button.rb +2 -2
- data/android_tests/lib/android/specs/android/element/generic.rb +1 -2
- data/android_tests/lib/android/specs/android/element/text.rb +2 -3
- data/android_tests/lib/android/specs/android/element/textfield.rb +2 -2
- data/android_tests/lib/android/specs/android/helper.rb +5 -3
- data/android_tests/lib/android/specs/android/patch.rb +2 -2
- data/android_tests/lib/android/specs/common/device.rb +16 -9
- data/android_tests/lib/android/specs/common/device_touchaction.rb +5 -2
- data/android_tests/lib/android/specs/common/element/window.rb +1 -1
- data/android_tests/lib/android/specs/common/helper.rb +14 -15
- data/android_tests/lib/android/specs/common/patch.rb +11 -9
- data/android_tests/lib/android/specs/common/version.rb +3 -3
- data/android_tests/lib/android/specs/common/web_context.rb +2 -3
- data/android_tests/lib/android/specs/driver.rb +38 -29
- data/android_tests/lib/android/specs/install.rb +3 -3
- data/android_tests/lib/format.rb +6 -8
- data/android_tests/lib/run.rb +25 -17
- data/android_tests/readme.md +4 -2
- data/appium_lib.gemspec +13 -11
- data/contributing.md +1 -1
- data/docs/android_docs.md +358 -274
- data/docs/ios_docs.md +333 -270
- data/docs/migration.md +10 -0
- data/docs_gen/make_docs.rb +3 -1
- data/ios_tests/Gemfile +1 -1
- data/ios_tests/Rakefile +17 -10
- data/ios_tests/appium.txt +1 -1
- data/ios_tests/lib/common.rb +8 -4
- data/ios_tests/lib/format.rb +5 -7
- data/ios_tests/lib/ios/specs/common/element/window.rb +1 -1
- data/ios_tests/lib/ios/specs/common/helper.rb +40 -39
- data/ios_tests/lib/ios/specs/common/patch.rb +15 -11
- data/ios_tests/lib/ios/specs/common/version.rb +3 -3
- data/ios_tests/lib/ios/specs/common/web_context.rb +1 -2
- data/ios_tests/lib/ios/specs/device/device.rb +7 -7
- data/ios_tests/lib/ios/specs/device/multi_touch.rb +6 -8
- data/ios_tests/lib/ios/specs/device/touch_actions.rb +12 -12
- data/ios_tests/lib/ios/specs/driver.rb +23 -22
- data/ios_tests/lib/ios/specs/ios/element/alert.rb +6 -2
- data/ios_tests/lib/ios/specs/ios/element/button.rb +2 -2
- data/ios_tests/lib/ios/specs/ios/element/generic.rb +1 -1
- data/ios_tests/lib/ios/specs/ios/element/text.rb +4 -1
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +6 -6
- data/ios_tests/lib/ios/specs/ios/helper.rb +5 -5
- data/ios_tests/lib/ios/specs/ios/patch.rb +2 -2
- data/ios_tests/lib/run.rb +1 -1
- data/ios_tests/readme.md +3 -3
- data/ios_tests/upload/sauce_storage.rb +8 -8
- data/ios_tests/upload/upload.rb +1 -1
- data/lib/appium_lib/android/client_xpath.rb +7 -7
- data/lib/appium_lib/android/element/alert.rb +2 -2
- data/lib/appium_lib/android/element/button.rb +16 -16
- data/lib/appium_lib/android/element/generic.rb +12 -13
- data/lib/appium_lib/android/element/text.rb +5 -5
- data/lib/appium_lib/android/element/textfield.rb +5 -5
- data/lib/appium_lib/android/helper.rb +82 -52
- data/lib/appium_lib/android/mobile_methods.rb +2 -2
- data/lib/appium_lib/android/patch.rb +3 -3
- data/lib/appium_lib/common/element/window.rb +1 -1
- data/lib/appium_lib/common/helper.rb +30 -35
- data/lib/appium_lib/common/patch.rb +22 -20
- data/lib/appium_lib/common/version.rb +3 -3
- data/lib/appium_lib/common/wait.rb +9 -10
- data/lib/appium_lib/device/device.rb +39 -33
- data/lib/appium_lib/device/multi_touch.rb +5 -7
- data/lib/appium_lib/device/touch_actions.rb +14 -15
- data/lib/appium_lib/driver.rb +97 -76
- data/lib/appium_lib/ios/element/alert.rb +1 -1
- data/lib/appium_lib/ios/element/button.rb +5 -5
- data/lib/appium_lib/ios/element/generic.rb +5 -6
- data/lib/appium_lib/ios/element/text.rb +5 -5
- data/lib/appium_lib/ios/element/textfield.rb +15 -15
- data/lib/appium_lib/ios/helper.rb +103 -90
- data/lib/appium_lib/ios/mobile_methods.rb +2 -2
- data/lib/appium_lib/ios/patch.rb +4 -4
- data/lib/appium_lib/logger.rb +7 -5
- data/lib/appium_lib/rails/duplicable.rb +3 -1
- data/readme.md +7 -1
- data/release_notes.md +152 -0
- metadata +28 -54
data/docs/migration.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### Breaking Changes in 7.0
|
2
|
+
|
3
|
+
Requires appium 1.4.0-beta or newer for iOS helper methods. appium_lib no longer automatically promotes methods on minispec. To restore the old behavior use: `Appium.promote_appium_methods ::Minitest::Spec`
|
4
|
+
|
5
|
+
The implicit wait now defaults to zero. To restore the old behavior, use `set_wait 30`.
|
6
|
+
|
7
|
+
Old | New
|
8
|
+
:--|:--
|
9
|
+
`installed?` | `app_installed?`
|
10
|
+
|
1
11
|
### Breaking Changes in 5.0
|
2
12
|
|
3
13
|
Old | New
|
data/docs_gen/make_docs.rb
CHANGED
@@ -30,13 +30,15 @@ def mobj_to_md obj
|
|
30
30
|
out += "> #{obj.signature}\n\n"
|
31
31
|
out += "#{obj.docstring}\n\n"
|
32
32
|
|
33
|
+
# puts "Processing: #{obj.name} #{method_path}"
|
33
34
|
|
34
35
|
indent = space 5
|
35
36
|
params = obj.tags.select { |tag| tag.tag_name == 'param' }
|
36
37
|
if !params.empty?
|
37
38
|
out += "__Parameters:__\n\n"
|
38
39
|
params.each do |param|
|
39
|
-
|
40
|
+
param_types = param.types ? "[#{param.types.join ', '}] " : ''
|
41
|
+
out += indent + param_types
|
40
42
|
out += "#{param.name} - #{param.text}\n\n"
|
41
43
|
end
|
42
44
|
end
|
data/ios_tests/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
|
-
gemspec path: File.expand_path('../../', __FILE__) # __dir__ fails on 1.9
|
2
|
+
gemspec path: File.expand_path('../../', __FILE__) # __dir__ fails on 1.9
|
data/ios_tests/Rakefile
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
+
require 'rubocop/rake_task'
|
3
4
|
|
4
|
-
task :
|
5
|
+
task default: :ios
|
5
6
|
|
6
7
|
# Run sh and ignore exception
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
8
|
+
# rubocop:disable Lint/HandleExceptions
|
9
|
+
def run_sh(cmd)
|
10
|
+
sh cmd
|
11
|
+
rescue
|
12
12
|
end
|
13
13
|
|
14
14
|
# Run cmd. On failure run install and try again.
|
15
|
-
def bash
|
15
|
+
def bash(cmd)
|
16
16
|
sh cmd do |successful, result|
|
17
17
|
# exitstatus 7 means bundle install failed
|
18
18
|
# exitstatus 1 means the test failed
|
19
|
-
if !successful && result.exitstatus
|
19
|
+
if !successful && result.exitstatus == 7
|
20
20
|
Rake::Task['install'].execute
|
21
21
|
run_sh cmd
|
22
22
|
end
|
@@ -31,13 +31,13 @@ end
|
|
31
31
|
# Run all tests with:
|
32
32
|
# rake ios
|
33
33
|
desc 'Run the iOS tests'
|
34
|
-
task :ios, :args, :test_file do |
|
34
|
+
task :ios, :args, :test_file do |_args, test_file|
|
35
35
|
# rake android['ok']
|
36
36
|
# args = android
|
37
37
|
# test_file = {:args=>"ok"}
|
38
38
|
test_file = test_file[:args]
|
39
39
|
cmd = 'bundle exec ruby ./lib/run.rb ios'
|
40
|
-
cmd += %
|
40
|
+
cmd += %( "#{test_file}") if test_file
|
41
41
|
bash cmd
|
42
42
|
end
|
43
43
|
|
@@ -45,3 +45,10 @@ desc 'Run bundle install'
|
|
45
45
|
task :install do
|
46
46
|
sh 'bundle install'
|
47
47
|
end
|
48
|
+
|
49
|
+
desc 'Execute RuboCop static code analysis'
|
50
|
+
RuboCop::RakeTask.new(:rubocop) do |t|
|
51
|
+
t.patterns = %w(**/*.rb)
|
52
|
+
t.options = %w(-D)
|
53
|
+
t.fail_on_error = false
|
54
|
+
end
|
data/ios_tests/appium.txt
CHANGED
data/ios_tests/lib/common.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
# common methods
|
3
|
-
def back_click(opts={})
|
4
|
-
opts
|
3
|
+
def back_click(opts = {})
|
4
|
+
opts ||= {}
|
5
5
|
search_wait = opts.fetch(:wait, 60 * 1.7)
|
6
6
|
# iOS may have multiple 'back' buttons
|
7
7
|
# select the first displayed? back button.
|
@@ -17,7 +17,11 @@ end
|
|
17
17
|
|
18
18
|
def go_to_textfields
|
19
19
|
screen.must_equal catalog
|
20
|
-
wait_true
|
20
|
+
wait_true do
|
21
|
+
text('textfield').click
|
22
|
+
screen == 'TextFields' # wait for screen transition
|
23
|
+
end
|
24
|
+
|
21
25
|
screen.must_equal 'TextFields'
|
22
26
|
end
|
23
27
|
|
@@ -27,4 +31,4 @@ end
|
|
27
31
|
|
28
32
|
def catalog
|
29
33
|
'UICatalog'
|
30
|
-
end
|
34
|
+
end
|
data/ios_tests/lib/format.rb
CHANGED
@@ -15,11 +15,9 @@ list.split("\n").each do |method|
|
|
15
15
|
puts
|
16
16
|
end
|
17
17
|
|
18
|
-
=begin
|
19
18
|
# for Pry
|
20
|
-
class Object
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
=end
|
19
|
+
# class Object
|
20
|
+
# def must_equal b
|
21
|
+
# raise 'not equal' unless self == b
|
22
|
+
# end
|
23
|
+
# end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# rake ios[common/helper]
|
2
2
|
describe 'common/helper.rb' do
|
3
|
-
|
4
3
|
def before_first
|
5
4
|
screen.must_equal catalog
|
6
5
|
end
|
@@ -11,12 +10,10 @@ describe 'common/helper.rb' do
|
|
11
10
|
|
12
11
|
wait_opts = { timeout: 0.2, interval: 0.2 } # max_wait, interval
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
must_not_raise is a no-op.
|
19
|
-
=end
|
13
|
+
# There's no `must_not_raise` as the opposite of must_raise
|
14
|
+
#
|
15
|
+
# By default code is expected to not raise exceptions.
|
16
|
+
# must_not_raise is a no-op.
|
20
17
|
|
21
18
|
# wait is a success unless an error is raised
|
22
19
|
# max_wait=0 is infinity to use 0.1
|
@@ -27,12 +24,12 @@ must_not_raise is a no-op.
|
|
27
24
|
wait(wait_opts) { nil }
|
28
25
|
|
29
26
|
# failed wait should error
|
30
|
-
proc { wait(wait_opts) {
|
27
|
+
proc { wait(wait_opts) { fail } }.must_raise Selenium::WebDriver::Error::TimeOutError
|
31
28
|
|
32
29
|
# regular rescue will not handle exceptions outside of StandardError hierarchy
|
33
30
|
# must rescue Exception explicitly to rescue everything
|
34
|
-
proc { wait(wait_opts) {
|
35
|
-
proc { wait(timeout: 0.2, interval: 0.0) {
|
31
|
+
proc { wait(wait_opts) { fail NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
|
32
|
+
proc { wait(timeout: 0.2, interval: 0.0) { fail NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
|
36
33
|
|
37
34
|
# invalid keys are rejected
|
38
35
|
proc { wait(invalidkey: 2) { true } }.must_raise RuntimeError
|
@@ -43,8 +40,8 @@ must_not_raise is a no-op.
|
|
43
40
|
ignore { true }
|
44
41
|
ignore { false }
|
45
42
|
ignore { nil }
|
46
|
-
ignore {
|
47
|
-
ignore {
|
43
|
+
ignore { fail }
|
44
|
+
ignore { fail NoMemoryError }
|
48
45
|
end
|
49
46
|
|
50
47
|
# wait_true is a success unless the value is not true
|
@@ -57,12 +54,13 @@ must_not_raise is a no-op.
|
|
57
54
|
proc { wait_true(wait_opts) { nil } }.must_raise Selenium::WebDriver::Error::TimeOutError
|
58
55
|
|
59
56
|
# raise should error
|
60
|
-
proc { wait_true(wait_opts) {
|
57
|
+
proc { wait_true(wait_opts) { fail } }.must_raise Selenium::WebDriver::Error::TimeOutError
|
61
58
|
|
62
59
|
# regular rescue will not handle exceptions outside of StandardError hierarchy
|
63
60
|
# must rescue Exception explicitly to rescue everything
|
64
|
-
proc { wait_true(wait_opts) {
|
65
|
-
proc { wait_true(timeout: 0.2, interval: 0.0) {
|
61
|
+
proc { wait_true(wait_opts) { fail NoMemoryError } }.must_raise Selenium::WebDriver::Error::TimeOutError
|
62
|
+
proc { wait_true(timeout: 0.2, interval: 0.0) { fail NoMemoryError } }
|
63
|
+
.must_raise Selenium::WebDriver::Error::TimeOutError
|
66
64
|
|
67
65
|
# invalid keys are rejected
|
68
66
|
proc { wait_true(invalidkey: 2) { true } }.must_raise RuntimeError
|
@@ -74,7 +72,11 @@ must_not_raise is a no-op.
|
|
74
72
|
# start page
|
75
73
|
tag('UIANavigationBar').name.must_equal 'UICatalog'
|
76
74
|
# nav to new page.
|
77
|
-
wait_true
|
75
|
+
wait_true do
|
76
|
+
text('buttons').click
|
77
|
+
tag('UIANavigationBar').name == 'Buttons'
|
78
|
+
end
|
79
|
+
|
78
80
|
tag('UIANavigationBar').name.must_equal 'Buttons'
|
79
81
|
# go back
|
80
82
|
back_click
|
@@ -84,7 +86,7 @@ must_not_raise is a no-op.
|
|
84
86
|
|
85
87
|
t 'session_id' do
|
86
88
|
# Sauce doesn't return '-' so make them optional.
|
87
|
-
session_id.must_match
|
89
|
+
session_id.must_match(/\h{8}-?\h{4}-?\h{4}-?\h{4}-?\h{12}/)
|
88
90
|
end
|
89
91
|
|
90
92
|
t 'xpath' do
|
@@ -103,11 +105,11 @@ must_not_raise is a no-op.
|
|
103
105
|
ele_index('UIAStaticText', 2).name.must_equal uibutton_text
|
104
106
|
end
|
105
107
|
|
106
|
-
#
|
108
|
+
# TODO: 'string_attr_exact'
|
107
109
|
|
108
110
|
t 'find_ele_by_attr' do
|
109
111
|
el_id = find_ele_by_attr('UIAStaticText', 'name', uibutton_text).instance_variable_get :@id
|
110
|
-
el_id.must_match
|
112
|
+
el_id.must_match(/\d+/)
|
111
113
|
end
|
112
114
|
|
113
115
|
t 'find_eles_by_attr' do
|
@@ -123,7 +125,7 @@ must_not_raise is a no-op.
|
|
123
125
|
set_wait
|
124
126
|
end
|
125
127
|
|
126
|
-
#
|
128
|
+
# TODO: 'string_attr_include'
|
127
129
|
|
128
130
|
t 'find_ele_by_attr_include' do
|
129
131
|
el_text = find_ele_by_attr_include('UIAStaticText', :name, 'button').text
|
@@ -181,23 +183,22 @@ must_not_raise is a no-op.
|
|
181
183
|
# 8 local. 9 on sauce.
|
182
184
|
get_page_class.split("\n").length.must_be :>=, 8
|
183
185
|
end
|
184
|
-
|
185
|
-
|
186
|
-
get_page_class
|
187
|
-
page_class
|
188
|
-
tag
|
189
|
-
tags
|
190
|
-
px_to_window_rel
|
191
|
-
lazy_load_strings
|
192
|
-
xml_keys
|
193
|
-
xml_values
|
194
|
-
resolve_id
|
195
|
-
string_visible_contains
|
196
|
-
xpath_visible_contains
|
197
|
-
xpaths_visible_contains
|
198
|
-
string_visible_exact
|
199
|
-
xpath_visible_exact
|
200
|
-
xpaths_visible_exact
|
201
|
-
raise_no_element_error
|
202
|
-
=end
|
186
|
+
|
187
|
+
# TODO: write tests
|
188
|
+
# get_page_class
|
189
|
+
# page_class
|
190
|
+
# tag
|
191
|
+
# tags
|
192
|
+
# px_to_window_rel
|
193
|
+
# lazy_load_strings
|
194
|
+
# xml_keys
|
195
|
+
# xml_values
|
196
|
+
# resolve_id
|
197
|
+
# string_visible_contains
|
198
|
+
# xpath_visible_contains
|
199
|
+
# xpaths_visible_contains
|
200
|
+
# string_visible_exact
|
201
|
+
# xpath_visible_exact
|
202
|
+
# xpaths_visible_exact
|
203
|
+
# raise_no_element_error
|
203
204
|
end
|
@@ -1,8 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
# rubocop:disable Lint/RescueException
|
2
|
+
# rubocop:disable Metrics/LineLength
|
3
|
+
|
4
|
+
#
|
5
|
+
# Skip:
|
6
|
+
# status # status patches are already tested in driver.rb
|
7
|
+
# raw_execute # debug output for Pry
|
8
|
+
#
|
9
|
+
|
6
10
|
# rake ios[common/patch]
|
7
11
|
describe 'common/patch.rb' do
|
8
12
|
def before_first
|
@@ -13,10 +17,10 @@ describe 'common/patch.rb' do
|
|
13
17
|
before_first
|
14
18
|
end
|
15
19
|
|
16
|
-
# Attributes are busted in Android.
|
17
|
-
# Blocked on https://github.com/appium/appium/issues/628
|
20
|
+
# Attributes are busted in Android.
|
21
|
+
# Blocked on https://github.com/appium/appium/issues/628
|
18
22
|
describe 'Selenium::WebDriver::Element methods' do
|
19
|
-
#
|
23
|
+
# TODO: t 'value' do; end
|
20
24
|
|
21
25
|
t 'name' do
|
22
26
|
first_text.name.must_equal 'UICatalog'
|
@@ -39,12 +43,12 @@ describe 'common/patch.rb' do
|
|
39
43
|
begin
|
40
44
|
set_wait 0
|
41
45
|
find_element(:css, 'ok')
|
42
|
-
rescue Exception => e
|
46
|
+
rescue Exception => e
|
43
47
|
value = e.message
|
44
48
|
ensure
|
45
49
|
set_wait
|
46
50
|
end
|
47
|
-
value.must_equal
|
51
|
+
value.must_equal 'Invalid locator strategy: css selector'
|
48
52
|
end
|
49
53
|
end
|
50
|
-
end
|
54
|
+
end
|
@@ -8,10 +8,10 @@ describe 'version.rb' do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
t '::Appium::VERSION' do
|
11
|
-
::Appium::VERSION.must_match
|
11
|
+
::Appium::VERSION.must_match(/(\d+)\.(\d+).(\d+)/)
|
12
12
|
end
|
13
13
|
|
14
14
|
t '::Appium::DATE' do
|
15
|
-
::Appium::DATE.must_match
|
15
|
+
::Appium::DATE.must_match(/(\d+)\-(\d+)\-(\d+)/)
|
16
16
|
end
|
17
|
-
end
|
17
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# Tests specifically for areas where the web_context differs in behaviour
|
2
2
|
describe 'the web context' do
|
3
|
-
|
4
3
|
t 'get_android_inspect' do
|
5
4
|
text('Web').click
|
6
5
|
set_context 'WEBVIEW'
|
7
6
|
current_context.must_equal 'WEBVIEW_1'
|
8
|
-
sleep 1 #Give a chance to load
|
7
|
+
sleep 1 # Give a chance to load
|
9
8
|
page.start_with?("\nhtml\n").must_equal true
|
10
9
|
end
|
11
10
|
|
@@ -6,7 +6,7 @@ describe 'device/device' do
|
|
6
6
|
# go back to the main page
|
7
7
|
def go_back
|
8
8
|
back
|
9
|
-
wait { !
|
9
|
+
wait { !exists { id 'ArrowButton' } } # successfully transitioned back
|
10
10
|
end
|
11
11
|
|
12
12
|
t 'before_first' do
|
@@ -27,8 +27,8 @@ describe 'device/device' do
|
|
27
27
|
screen.must_equal catalog
|
28
28
|
end
|
29
29
|
|
30
|
-
t '
|
31
|
-
installed =
|
30
|
+
t 'app_installed' do
|
31
|
+
installed = app_installed? 'Derrp'
|
32
32
|
installed.must_equal false
|
33
33
|
end
|
34
34
|
|
@@ -47,7 +47,7 @@ describe 'device/device' do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
t 'available_contexts' do
|
50
|
-
available_contexts.must_equal [
|
50
|
+
available_contexts.must_equal ['NATIVE_APP']
|
51
51
|
end
|
52
52
|
|
53
53
|
t 'current_context' do
|
@@ -60,8 +60,8 @@ describe 'device/device' do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
t 'app_strings' do
|
63
|
-
app_strings.must_include
|
64
|
-
app_strings('en').must_include
|
63
|
+
app_strings.must_include 'SearchBarExplain'
|
64
|
+
app_strings('en').must_include 'SearchBarExplain'
|
65
65
|
end
|
66
66
|
|
67
67
|
t 'action_chain' do
|
@@ -91,4 +91,4 @@ describe 'device/device' do
|
|
91
91
|
data = pull_folder 'Library/AddressBook'
|
92
92
|
data.length.must_be :>, 1
|
93
93
|
end
|
94
|
-
end
|
94
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
describe 'device/touch_actions' do
|
2
2
|
t {} # place holder test
|
3
3
|
end
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
|
5
|
+
# TODO: write tests
|
6
|
+
#
|
7
|
+
# move_to
|
8
|
+
# long_press
|
9
|
+
# press
|
10
|
+
# release
|
11
|
+
# tap
|
12
|
+
# wait
|
13
|
+
# swipe
|
14
|
+
# perform
|
15
|
+
# cancel
|