appium_lib 0.24.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +17 -8
- data/android_tests/Gemfile +1 -0
- data/android_tests/LICENSE-2.0.txt +202 -0
- data/android_tests/Rakefile +61 -0
- data/android_tests/api.apk +0 -0
- data/android_tests/appium.txt +3 -0
- data/android_tests/flaky.txt +1 -0
- data/android_tests/lib/android/specs/android/dynamic.rb +5 -0
- data/android_tests/lib/android/specs/android/element/alert.rb +41 -0
- data/android_tests/lib/android/specs/android/element/button.rb +55 -0
- data/android_tests/lib/android/specs/android/element/generic.rb +48 -0
- data/android_tests/lib/android/specs/android/element/text.rb +39 -0
- data/android_tests/lib/android/specs/android/element/textfield.rb +60 -0
- data/android_tests/lib/android/specs/android/helper.rb +80 -0
- data/android_tests/lib/android/specs/android/patch.rb +14 -0
- data/android_tests/lib/android/specs/common/device.rb +117 -0
- data/android_tests/lib/android/specs/common/element/window.rb +9 -0
- data/android_tests/lib/android/specs/common/helper.rb +112 -0
- data/android_tests/lib/android/specs/common/patch.rb +69 -0
- data/android_tests/lib/android/specs/common/version.rb +9 -0
- data/android_tests/lib/android/specs/driver.rb +174 -0
- data/android_tests/lib/format.rb +49 -0
- data/android_tests/lib/run.rb +72 -0
- data/android_tests/readme.md +27 -0
- data/appium_lib.gemspec +8 -5
- data/docs/android_docs.md +1052 -716
- data/docs/ios_docs.md +657 -834
- data/docs_gen/make_docs.rb +1 -3
- data/ios_tests/Gemfile +1 -0
- data/ios_tests/LICENSE-2.0.txt +202 -0
- data/ios_tests/Rakefile +47 -0
- data/ios_tests/UICatalog.app.zip +0 -0
- data/ios_tests/UICatalog.app/12-6AM.png +0 -0
- data/ios_tests/UICatalog.app/12-6PM.png +0 -0
- data/ios_tests/UICatalog.app/6-12AM.png +0 -0
- data/ios_tests/UICatalog.app/6-12PM.png +0 -0
- data/ios_tests/UICatalog.app/Default-568h@2x.png +0 -0
- data/ios_tests/UICatalog.app/Default@2x.png +0 -0
- data/ios_tests/UICatalog.app/Info.plist +0 -0
- data/ios_tests/UICatalog.app/PkgInfo +1 -0
- data/ios_tests/UICatalog.app/UIButton_custom.png +0 -0
- data/ios_tests/UICatalog.app/UICatalog +0 -0
- data/ios_tests/UICatalog.app/blueButton.png +0 -0
- data/ios_tests/UICatalog.app/bookmarkImage.png +0 -0
- data/ios_tests/UICatalog.app/bookmarkImageHighlighted.png +0 -0
- data/ios_tests/UICatalog.app/divider.png +0 -0
- data/ios_tests/UICatalog.app/en.lproj/AlertsViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ButtonsViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ControlsViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ImagesViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/Localizable.strings +0 -0
- data/ios_tests/UICatalog.app/en.lproj/MainWindow.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/PickerViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/SearchBarController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/SegmentViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/TextFieldController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/TextViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/ToolbarViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/TransitionViewController.nib +0 -0
- data/ios_tests/UICatalog.app/en.lproj/WebViewController.nib +0 -0
- data/ios_tests/UICatalog.app/orangeslide.png +0 -0
- data/ios_tests/UICatalog.app/scene1.jpg +0 -0
- data/ios_tests/UICatalog.app/scene2.jpg +0 -0
- data/ios_tests/UICatalog.app/scene3.jpg +0 -0
- data/ios_tests/UICatalog.app/scene4.jpg +0 -0
- data/ios_tests/UICatalog.app/scene5.jpg +0 -0
- data/ios_tests/UICatalog.app/searchBarBackground.png +0 -0
- data/ios_tests/UICatalog.app/segment_check.png +0 -0
- data/ios_tests/UICatalog.app/segment_search.png +0 -0
- data/ios_tests/UICatalog.app/segment_tools.png +0 -0
- data/ios_tests/UICatalog.app/segmentedBackground.png +0 -0
- data/ios_tests/UICatalog.app/slider_ball.png +0 -0
- data/ios_tests/UICatalog.app/toolbarBackground.png +0 -0
- data/ios_tests/UICatalog.app/whiteButton.png +0 -0
- data/ios_tests/UICatalog.app/yellowslide.png +0 -0
- data/ios_tests/appium.txt +3 -0
- data/ios_tests/flaky.txt +1 -0
- data/ios_tests/lib/format.rb +25 -0
- data/ios_tests/lib/ios/specs/common/element/window.rb +15 -0
- data/ios_tests/lib/ios/specs/common/helper.rb +204 -0
- data/ios_tests/lib/ios/specs/common/patch.rb +50 -0
- data/ios_tests/lib/ios/specs/common/version.rb +17 -0
- data/ios_tests/lib/ios/specs/device/device.rb +82 -0
- data/ios_tests/lib/ios/specs/device/multi_touch.rb +12 -0
- data/ios_tests/lib/ios/specs/device/touch_actions.rb +15 -0
- data/ios_tests/lib/ios/specs/driver.rb +203 -0
- data/ios_tests/lib/ios/specs/ios/element/alert.rb +48 -0
- data/ios_tests/lib/ios/specs/ios/element/button.rb +58 -0
- data/ios_tests/lib/ios/specs/ios/element/generic.rb +35 -0
- data/ios_tests/lib/ios/specs/ios/element/text.rb +54 -0
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +123 -0
- data/ios_tests/lib/ios/specs/ios/helper.rb +27 -0
- data/ios_tests/lib/ios/specs/ios/patch.rb +30 -0
- data/ios_tests/lib/run.rb +106 -0
- data/ios_tests/readme.md +30 -0
- data/ios_tests/upload/sauce_storage.rb +64 -0
- data/ios_tests/upload/upload.rb +6 -0
- data/lib/appium_lib.rb +4 -14
- data/lib/appium_lib/android/dynamic.rb +30 -32
- data/lib/appium_lib/android/element/alert.rb +34 -33
- data/lib/appium_lib/android/element/button.rb +91 -0
- data/lib/appium_lib/android/element/generic.rb +51 -146
- data/lib/appium_lib/android/element/text.rb +54 -0
- data/lib/appium_lib/android/element/textfield.rb +46 -41
- data/lib/appium_lib/android/helper.rb +248 -417
- data/lib/appium_lib/android/mobile_methods.rb +17 -0
- data/lib/appium_lib/android/patch.rb +9 -8
- data/lib/appium_lib/awesome_print/ostruct.rb +33 -0
- data/lib/appium_lib/common/element/window.rb +9 -8
- data/lib/appium_lib/common/helper.rb +182 -243
- data/lib/appium_lib/common/patch.rb +65 -79
- data/lib/appium_lib/common/version.rb +2 -3
- data/lib/appium_lib/device/device.rb +339 -0
- data/lib/appium_lib/device/multi_touch.rb +94 -0
- data/lib/appium_lib/device/touch_actions.rb +142 -0
- data/lib/appium_lib/driver.rb +217 -306
- data/lib/appium_lib/ios/element/alert.rb +16 -92
- data/lib/appium_lib/ios/element/button.rb +55 -0
- data/lib/appium_lib/ios/element/generic.rb +27 -160
- data/lib/appium_lib/ios/element/text.rb +54 -0
- data/lib/appium_lib/ios/element/textfield.rb +78 -65
- data/lib/appium_lib/ios/helper.rb +300 -190
- data/lib/appium_lib/ios/mobile_methods.rb +17 -0
- data/lib/appium_lib/ios/patch.rb +55 -41
- data/lib/appium_lib/logger.rb +13 -0
- data/lib/appium_lib/rails/duplicable.rb +116 -0
- data/readme.md +6 -1
- data/release_notes.md +118 -0
- metadata +170 -12
- data/lib/appium_lib/common/element/button.rb +0 -83
- data/lib/appium_lib/common/element/text.rb +0 -61
@@ -0,0 +1,94 @@
|
|
1
|
+
module Appium
|
2
|
+
|
3
|
+
# MultiTouch actions allow for multiple touches to happen at the same time,
|
4
|
+
# for instance, to simulate multiple finger swipes.
|
5
|
+
#
|
6
|
+
# Create a series of touch actions by themselves (without a `prepare()`), then
|
7
|
+
# add to a new MultiTouch action. When ready, call `prepare()` and all
|
8
|
+
# actions will be executed simultaneously.
|
9
|
+
#
|
10
|
+
# ```ruby
|
11
|
+
# action_1 = TouchAction.new.press(x: 45, y: 100).wait(5).release
|
12
|
+
# action_2 = TouchAction.new.tap(element: el, x: 50, y:5, count: 3).release
|
13
|
+
#
|
14
|
+
# multi_touch_action = MultiTouch.new
|
15
|
+
# multi_touch_action.add action_1
|
16
|
+
# multi_touch_action.add action_2
|
17
|
+
# multi_touch_action.perform
|
18
|
+
class MultiTouch
|
19
|
+
class << self
|
20
|
+
|
21
|
+
# Convenience method for pinching the screen.
|
22
|
+
# Places two fingers at the edges of the screen and brings them together.
|
23
|
+
# @param percentage (int) The percent size by which to shrink the screen when pinched.
|
24
|
+
# @param auto_perform (boolean) Whether to perform the action immediately (default true)
|
25
|
+
#
|
26
|
+
# ```ruby
|
27
|
+
# action = pinch 75 #=> Pinch the screen from the top right and bottom left corners
|
28
|
+
# action.perform #=> to 25% of its size.
|
29
|
+
# ```
|
30
|
+
def pinch(percentage=25, auto_perform=true)
|
31
|
+
raise ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
|
32
|
+
|
33
|
+
p = Float(percentage) / 100
|
34
|
+
i = 1 - p
|
35
|
+
|
36
|
+
top = TouchAction.new
|
37
|
+
top.swipe start_x: 1.0, start_y: 0.0, end_x: i, end_y: i, duration: 1
|
38
|
+
|
39
|
+
bottom = TouchAction.new
|
40
|
+
bottom.swipe(start_x: 0.0, start_y: 1.0, end_x: p, end_y: p, duration: 1)
|
41
|
+
|
42
|
+
pinch = MultiTouch.new
|
43
|
+
pinch.add top
|
44
|
+
pinch.add bottom
|
45
|
+
return pinch unless auto_perform
|
46
|
+
pinch.perform
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convenience method for zooming the screen.
|
50
|
+
# Places two fingers at the edges of the screen and brings them together.
|
51
|
+
# @param percentage (int) The percent size by which to shrink the screen when pinched.
|
52
|
+
# @param auto_perform (boolean) Whether to perform the action immediately (default true)
|
53
|
+
#
|
54
|
+
# ```ruby
|
55
|
+
# action = zoom 200 #=> Zoom in the screen from the center until it doubles in size.
|
56
|
+
# action.perform
|
57
|
+
# ```
|
58
|
+
def zoom(percentage=200, auto_perform=true)
|
59
|
+
raise ArgumentError("Can't zoom to smaller then screen size.") if percentage < 100
|
60
|
+
|
61
|
+
p = 100 / Float(percentage)
|
62
|
+
i = 1 - p
|
63
|
+
|
64
|
+
top = TouchAction.new
|
65
|
+
top.swipe start_x: i, start_y: i, end_x: 1, end_y: 1, duration: 1
|
66
|
+
|
67
|
+
bottom = TouchAction.new
|
68
|
+
bottom.swipe start_x: p, start_y: p, end_x: 1, end_y: 1, duration: 1
|
69
|
+
|
70
|
+
zoom = MultiTouch.new
|
71
|
+
zoom.add top
|
72
|
+
zoom.add bottom
|
73
|
+
return zoom unless auto_perform
|
74
|
+
zoom.perform
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create a new multi-action
|
79
|
+
def initialize
|
80
|
+
@actions = []
|
81
|
+
end
|
82
|
+
|
83
|
+
# Add a touch_action to be performed
|
84
|
+
# @param chain (TouchAction) The action to add to the chain
|
85
|
+
def add(chain)
|
86
|
+
@actions << chain.actions
|
87
|
+
end
|
88
|
+
|
89
|
+
# Ask Appium to perform the actions
|
90
|
+
def perform
|
91
|
+
$driver.multi_touch @actions
|
92
|
+
end
|
93
|
+
end # class MultiTouch
|
94
|
+
end # module Appium
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Appium
|
2
|
+
|
3
|
+
# Perform a series of gestures, one after another. Gestures are chained
|
4
|
+
# together and only performed when `perform()` is called.
|
5
|
+
#
|
6
|
+
# Each method returns the object itself, so calls can be chained.
|
7
|
+
#
|
8
|
+
# ```ruby
|
9
|
+
# action = TouchAction.new.press(x: 45, y: 100).wait(5).release
|
10
|
+
# action.perform
|
11
|
+
class TouchAction
|
12
|
+
ACTIONS = [:move_to, :press_for_duration, :press, :release, :tap, :wait, :perform]
|
13
|
+
COMPLEX_ACTIONS = [:swipe]
|
14
|
+
|
15
|
+
class << self
|
16
|
+
COMPLEX_ACTIONS.each do |action|
|
17
|
+
define_method(action) do |opts|
|
18
|
+
auto_perform = opts.delete(:auto_perform) {|k| true}
|
19
|
+
ta = TouchAction.new
|
20
|
+
ta.send(action, opts)
|
21
|
+
return ta unless auto_perform
|
22
|
+
ta.perform
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :actions
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@actions = []
|
31
|
+
end
|
32
|
+
|
33
|
+
# Move to the given co-ordinates.
|
34
|
+
# @option opts [integer] :x x co-ordinate to move to.
|
35
|
+
# @option opts [integer] :y y co-ordinate to move to.
|
36
|
+
# @option opts [WebDriver::Element] Element to scope this move within.
|
37
|
+
def move_to(opts)
|
38
|
+
opts = args_with_ele_ref(opts)
|
39
|
+
chain_method(:moveTo, opts)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Press down for a specific duration.
|
43
|
+
# @param element [WebDriver::Element] the element to press.
|
44
|
+
# @param x [integer] x co-ordinate to press on.
|
45
|
+
# @param y [integer] y co-ordinate to press on.
|
46
|
+
# @param duration [integer] Number of seconds to press.
|
47
|
+
def press_for_duration(element, x, y, duration)
|
48
|
+
@actions << {element: element.ref, x: x, y: y, duration: duration}
|
49
|
+
chain_method(:longPress, args)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Press a finger onto the screen. Finger will stay down until you call
|
53
|
+
# `release`.
|
54
|
+
#
|
55
|
+
# @option opts [WebDriver::Element] :element (Optional) Element to press within.
|
56
|
+
# @option opts [integer] :x x co-ordinate to press on
|
57
|
+
# @option opts [integer] :y y co-ordinate to press on
|
58
|
+
def press(opts)
|
59
|
+
args = opts.select {|k, v| [:element, :x, :y].include? k}
|
60
|
+
args = args_with_ele_ref(args)
|
61
|
+
chain_method(:press, args)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Remove a finger from the screen.
|
65
|
+
#
|
66
|
+
# @option opts [WebDriver::Element] :element (Optional) Element to release from.
|
67
|
+
# @option opts [integer] :x x co-ordinate to release from
|
68
|
+
# @option opts [integer] :y y co-ordinate to release from
|
69
|
+
def release(opts=nil)
|
70
|
+
args = args_with_ele_ref(opts) if opts
|
71
|
+
chain_method(:release, args)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Touch a point on the screen
|
75
|
+
#
|
76
|
+
# @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
|
77
|
+
# @option opts [integer] :x x co-ordinate to tap
|
78
|
+
# @option opts [integer] :y y co-ordinate to tap
|
79
|
+
# @option opts [integer] :fingers how many fingers to tap with (Default 1)
|
80
|
+
def tap(opts)
|
81
|
+
opts[:count] = opts.delete(:fingers) if opts[:fingers]
|
82
|
+
opts_with_defaults = {count: 1}.merge opts
|
83
|
+
chain_method(:tap, opts_with_defaults)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Pause for a number of seconds before the next action
|
87
|
+
# @param seconds [integer] Number of seconds to pause for
|
88
|
+
def wait(seconds)
|
89
|
+
args = {ms: seconds}
|
90
|
+
chain_method(:wait, args)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Convenience method to peform a swipe.
|
94
|
+
# @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
|
95
|
+
# @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
|
96
|
+
# @option opts [int] :end_x Where to end swiping, on the x axis. Default 0.
|
97
|
+
# @option opts [int] :end_y Where to end swiping, on the y axis. Default 0.
|
98
|
+
# @option opts [int] :duration How long the actual swipe takes to complete.
|
99
|
+
def swipe(opts)
|
100
|
+
start_x = opts.fetch :start_x, 0
|
101
|
+
start_y = opts.fetch :start_y, 0
|
102
|
+
end_x = opts.fetch :end_x, 0
|
103
|
+
end_y = opts.fetch :end_y, 0
|
104
|
+
duration = opts[:duration]
|
105
|
+
|
106
|
+
self.press x: start_x, y: start_y
|
107
|
+
self.wait(duration) if duration
|
108
|
+
self.move_to x: end_x, y: end_y
|
109
|
+
self.release
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# Ask the driver to perform all actions in this action chain.
|
114
|
+
def perform
|
115
|
+
$driver.touch_actions @actions
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# Does nothing, currently.
|
120
|
+
def cancel
|
121
|
+
@actions << {action: cancel}
|
122
|
+
$driver.touch_actions @actions
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def chain_method(method, args=nil)
|
129
|
+
if args
|
130
|
+
@actions << {action: method, options: args}
|
131
|
+
else
|
132
|
+
@actions << {action: method}
|
133
|
+
end
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
def args_with_ele_ref(args)
|
138
|
+
args[:element] = args[:element].ref if args.has_key? :element
|
139
|
+
args
|
140
|
+
end
|
141
|
+
end # class TouchAction
|
142
|
+
end # module Appium
|
data/lib/appium_lib/driver.rb
CHANGED
@@ -1,146 +1,152 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
=begin
|
3
|
-
Based on simple_test.rb
|
4
|
-
https://github.com/appium/appium/blob/82995f47408530c80c3376f4e07a1f649d96ba22/sample-code/examples/ruby/simple_test.rb
|
5
|
-
https://github.com/appium/appium/blob/c58eeb66f2d6fa3b9a89d188a2e657cca7cb300f/LICENSE
|
6
|
-
=end
|
7
|
-
|
8
1
|
require 'rubygems'
|
9
2
|
require 'ap'
|
3
|
+
require 'selenium-webdriver'
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
# patch ap
|
7
|
+
require_relative 'awesome_print/ostruct'
|
8
|
+
|
9
|
+
# common
|
10
|
+
require_relative 'common/helper'
|
11
|
+
require_relative 'common/patch'
|
12
|
+
require_relative 'common/version'
|
13
|
+
require_relative 'common/element/window'
|
14
|
+
|
15
|
+
# ios
|
16
|
+
require_relative 'ios/helper'
|
17
|
+
require_relative 'ios/patch'
|
18
|
+
|
19
|
+
require_relative 'ios/element/alert'
|
20
|
+
require_relative 'ios/element/button'
|
21
|
+
require_relative 'ios/element/generic'
|
22
|
+
require_relative 'ios/element/textfield'
|
23
|
+
require_relative 'ios/element/text'
|
24
|
+
require_relative 'ios/mobile_methods'
|
25
|
+
|
26
|
+
# android
|
27
|
+
require_relative 'android/dynamic'
|
28
|
+
require_relative 'android/helper'
|
29
|
+
require_relative 'android/patch'
|
30
|
+
require_relative 'android/element/alert'
|
31
|
+
require_relative 'android/element/button'
|
32
|
+
require_relative 'android/element/generic'
|
33
|
+
require_relative 'android/element/textfield'
|
34
|
+
require_relative 'android/element/text'
|
35
|
+
require_relative 'android/mobile_methods'
|
36
|
+
|
37
|
+
# device methods
|
38
|
+
require_relative 'device/device'
|
39
|
+
require_relative 'device/touch_actions'
|
40
|
+
require_relative 'device/multi_touch'
|
10
41
|
|
11
|
-
#
|
12
|
-
|
13
|
-
#
|
14
|
-
class
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
awesome_hash target.marshal_dump
|
42
|
+
# Fix uninitialized constant Minitest (NameError)
|
43
|
+
module Minitest
|
44
|
+
# Fix superclass mismatch for class Spec
|
45
|
+
class Runnable
|
46
|
+
end
|
47
|
+
class Test < Runnable
|
48
|
+
end
|
49
|
+
class Spec < Test
|
20
50
|
end
|
21
51
|
end
|
22
52
|
|
23
|
-
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
53
|
+
module Appium
|
54
|
+
# Load appium.txt (toml format)
|
55
|
+
# the basedir of this file + appium.txt is what's used
|
56
|
+
#
|
57
|
+
# ```
|
58
|
+
# [caps]
|
59
|
+
# app = "path/to/app"
|
60
|
+
#
|
61
|
+
# [appium_lib]
|
62
|
+
# port = 8080
|
63
|
+
# ```
|
64
|
+
#
|
65
|
+
# :app is expanded
|
66
|
+
# :require is expanded
|
67
|
+
# all keys are converted to symbols
|
68
|
+
#
|
69
|
+
# @param opts [Hash] file: '/path/to/appium.txt', verbose: true
|
70
|
+
# @return [hash] the symbolized hash with updated :app and :require keys
|
71
|
+
def self.load_appium_txt opts={}
|
72
|
+
raise 'opts must be a hash' unless opts.kind_of? Hash
|
73
|
+
raise 'opts must not be empty' if opts.empty?
|
74
|
+
|
75
|
+
file = opts[:file]
|
76
|
+
raise 'Must pass file' unless file
|
77
|
+
verbose = opts.fetch :verbose, false
|
78
|
+
|
79
|
+
parent_dir = File.dirname file
|
80
|
+
toml = File.expand_path File.join parent_dir, 'appium.txt'
|
81
|
+
puts "appium.txt path: #{toml}" if verbose
|
45
82
|
|
46
|
-
|
47
|
-
|
48
|
-
data = nil
|
83
|
+
toml_exists = File.exists? toml
|
84
|
+
puts "Exists? #{toml_exists}" if verbose
|
49
85
|
|
50
|
-
|
86
|
+
raise "toml doesn't exist #{toml}" unless toml_exists
|
51
87
|
require 'toml'
|
52
88
|
puts "Loading #{toml}" if verbose
|
53
89
|
|
54
|
-
# bash requires A="OK"
|
55
|
-
# toml requires A = "OK"
|
56
|
-
#
|
57
|
-
# A="OK" => A = "OK"
|
58
90
|
data = File.read toml
|
59
|
-
|
60
|
-
data = data.split("\n").map do |line|
|
61
|
-
line.sub /([^\s])\=/, "\\1 = "
|
62
|
-
end.join "\n"
|
63
|
-
|
64
91
|
data = TOML::Parser.new(data).parsed
|
92
|
+
# TOML creates string keys. must symbolize
|
93
|
+
data = Appium::symbolize_keys data
|
65
94
|
ap data unless data.empty? if verbose
|
66
95
|
|
67
|
-
|
68
|
-
|
69
|
-
'DEVICE'
|
70
|
-
|
71
|
-
# ensure app path is resolved correctly from the context of the .txt file
|
72
|
-
ENV['APP_PATH'] = Appium::Driver.absolute_app_path ENV['APP_PATH']
|
73
|
-
end
|
74
|
-
|
75
|
-
# return list of require files as an array
|
76
|
-
# nil if require doesn't exist
|
77
|
-
if data && data['require']
|
78
|
-
r = data['require']
|
79
|
-
r = r.kind_of?(Array) ? r : [ r ]
|
80
|
-
# ensure files are absolute
|
81
|
-
r.map! do |file|
|
82
|
-
file = file.include?(File::Separator) ? file :
|
83
|
-
File.join(parent_dir, file)
|
84
|
-
file = File.expand_path file
|
85
|
-
|
86
|
-
File.exists?(file) ? file : nil
|
96
|
+
if data && data[:caps] && data[:caps][:app]
|
97
|
+
data[:caps][:app] = Appium::Driver.absolute_app_path data[:caps][:app]
|
87
98
|
end
|
88
|
-
r.compact! # remove nils
|
89
|
-
|
90
|
-
files = []
|
91
99
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
100
|
+
# return list of require files as an array
|
101
|
+
# nil if require doesn't exist
|
102
|
+
if data && data[:appium_lib] && data[:appium_lib][:require]
|
103
|
+
r = data[:appium_lib][:require]
|
104
|
+
r = r.kind_of?(Array) ? r : [r]
|
105
|
+
# ensure files are absolute
|
106
|
+
r.map! do |file|
|
107
|
+
file = File.exists?(file) ? file :
|
108
|
+
File.join(parent_dir, file)
|
109
|
+
file = File.expand_path file
|
110
|
+
|
111
|
+
File.exists?(file) ? file : nil
|
98
112
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
113
|
+
r.compact! # remove nils
|
114
|
+
|
115
|
+
files = []
|
116
|
+
|
117
|
+
# now expand dirs
|
118
|
+
r.each do |item|
|
119
|
+
unless File.directory? item
|
120
|
+
# save file
|
121
|
+
files << item
|
122
|
+
next # only look inside folders
|
123
|
+
end
|
124
|
+
Dir.glob(File.expand_path(File.join(item, '**', '*.rb'))) do |file|
|
125
|
+
# do not add folders to the file list
|
126
|
+
files << File.expand_path(file) unless File.directory? file
|
127
|
+
end
|
102
128
|
end
|
129
|
+
|
130
|
+
# Must not sort files. File order is specified in appium.txt
|
131
|
+
data[:appium_lib][:require] = files
|
103
132
|
end
|
104
133
|
|
105
|
-
|
134
|
+
data
|
106
135
|
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# Fix uninitialized constant Minitest (NameError)
|
110
|
-
module Minitest
|
111
|
-
# Fix superclass mismatch for class Spec
|
112
|
-
class Runnable; end
|
113
|
-
class Test < Runnable; end
|
114
|
-
class Spec < Test; end
|
115
|
-
end
|
116
136
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# ios
|
131
|
-
require_relative 'ios/helper'
|
132
|
-
require_relative 'ios/patch'
|
133
|
-
require_relative 'ios/element/alert'
|
134
|
-
require_relative 'ios/element/generic'
|
135
|
-
require_relative 'ios/element/textfield'
|
136
|
-
|
137
|
-
# android
|
138
|
-
require_relative 'android/dynamic'
|
139
|
-
require_relative 'android/helper'
|
140
|
-
require_relative 'android/patch'
|
141
|
-
require_relative 'android/element/alert'
|
142
|
-
require_relative 'android/element/generic'
|
143
|
-
require_relative 'android/element/textfield'
|
137
|
+
# convert all keys (including nested) to symbols
|
138
|
+
#
|
139
|
+
# based on deep_symbolize_keys & deep_transform_keys from rails
|
140
|
+
# https://github.com/rails/docrails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/activesupport/lib/active_support/core_ext/hash/keys.rb#L84
|
141
|
+
def self.symbolize_keys hash
|
142
|
+
raise 'symbolize_keys requires a hash' unless hash.is_a? Hash
|
143
|
+
result = {}
|
144
|
+
hash.each do |key, value|
|
145
|
+
key = key.to_sym rescue key
|
146
|
+
result[key] = value.is_a?(Hash) ? symbolize_keys(value) : value
|
147
|
+
end
|
148
|
+
result
|
149
|
+
end
|
144
150
|
|
145
151
|
def self.promote_singleton_appium_methods main_module
|
146
152
|
raise 'Driver is nil' if $driver.nil?
|
@@ -171,7 +177,6 @@ module Appium
|
|
171
177
|
# ```ruby
|
172
178
|
# Appium.promote_appium_methods Object
|
173
179
|
# ```
|
174
|
-
|
175
180
|
def self.promote_appium_methods class_array
|
176
181
|
raise 'Driver is nil' if $driver.nil?
|
177
182
|
# Wrap single class into an array
|
@@ -185,9 +190,9 @@ module Appium
|
|
185
190
|
# Prefer existing method.
|
186
191
|
# super will invoke method missing on driver
|
187
192
|
super(*args, &block)
|
188
|
-
|
189
|
-
|
190
|
-
|
193
|
+
# minitest also defines a name method,
|
194
|
+
# so rescue argument error
|
195
|
+
# and call the name method on $driver
|
191
196
|
rescue NoMethodError, ArgumentError
|
192
197
|
$driver.send m, *args, &block if $driver.respond_to?(m)
|
193
198
|
end
|
@@ -201,37 +206,26 @@ module Appium
|
|
201
206
|
class Driver
|
202
207
|
@@loaded = false
|
203
208
|
|
204
|
-
|
205
|
-
|
206
|
-
:sauce_username, :sauce_access_key, :port, :debug,
|
207
|
-
:export_session, :device_cap, :compress_xml, :custom_url
|
209
|
+
# attr readers are promoted to global scope. To avoid clobbering, they're
|
210
|
+
# made available via the driver_attributes method
|
208
211
|
|
209
212
|
# The amount to sleep in seconds before every webdriver http call.
|
210
213
|
attr_accessor :global_webdriver_http_sleep
|
211
|
-
|
212
|
-
#
|
214
|
+
|
215
|
+
# Creates a new driver
|
213
216
|
#
|
214
217
|
# ```ruby
|
215
|
-
# # Options include:
|
216
|
-
# :app_path, :app_name, :app_package, :app_activity,
|
217
|
-
# :app_wait_activity, :sauce_username, :sauce_access_key,
|
218
|
-
# :port, :os, :debug
|
219
|
-
#
|
220
218
|
# require 'rubygems'
|
221
219
|
# require 'appium_lib'
|
222
220
|
#
|
221
|
+
# # platformName takes a string or a symbol.
|
222
|
+
#
|
223
223
|
# # Start iOS driver
|
224
|
-
#
|
225
|
-
# Appium::Driver.new(
|
224
|
+
# opts = { caps: { platformName: :ios, app: '/path/to/MyiOS.app' } }
|
225
|
+
# Appium::Driver.new(opts).start_driver
|
226
226
|
#
|
227
227
|
# # Start Android driver
|
228
|
-
#
|
229
|
-
# app_path: '/path/to/the.apk',
|
230
|
-
# app_package: 'com.example.pkg',
|
231
|
-
# app_activity: 'act.Start',
|
232
|
-
# app_wait_activity: 'act.Start'
|
233
|
-
# }
|
234
|
-
#
|
228
|
+
# opts = { caps: { platformName: :android, app: '/path/to/my.apk' } }
|
235
229
|
# Appium::Driver.new(apk).start_driver
|
236
230
|
# ```
|
237
231
|
#
|
@@ -240,75 +234,38 @@ module Appium
|
|
240
234
|
def initialize opts={}
|
241
235
|
# quit last driver
|
242
236
|
$driver.driver_quit if $driver
|
243
|
-
opts
|
244
|
-
tmp_opts = {}
|
245
|
-
|
246
|
-
# convert to downcased symbols
|
247
|
-
opts.each_pair { |k,v| tmp_opts[k.to_s.downcase.strip.intern] = v }
|
248
|
-
opts = tmp_opts
|
249
|
-
|
250
|
-
@raw_capabilities = opts.fetch(:raw, {})
|
237
|
+
raise 'opts must be a hash' unless opts.kind_of? Hash
|
251
238
|
|
252
|
-
|
239
|
+
opts = Appium::symbolize_keys opts
|
253
240
|
|
254
|
-
|
241
|
+
# default to {} to prevent nil.fetch and other nil errors
|
242
|
+
@caps = opts[:caps] || {}
|
243
|
+
appium_lib_opts = opts[:appium_lib] || {}
|
255
244
|
|
256
|
-
|
257
|
-
|
258
|
-
@
|
259
|
-
@
|
245
|
+
# appium_lib specific values
|
246
|
+
@custom_url = appium_lib_opts.fetch :server_url, false
|
247
|
+
@export_session = appium_lib_opts.fetch :export_session, false
|
248
|
+
@default_wait = appium_lib_opts.fetch :wait, 30
|
249
|
+
@last_waits = [@default_wait]
|
250
|
+
@sauce_username = appium_lib_opts.fetch :sauce_username, ENV['SAUCE_USERNAME']
|
251
|
+
@sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY']
|
252
|
+
@port = appium_lib_opts.fetch :port, 4723
|
260
253
|
|
261
254
|
# Path to the .apk, .app or .app.zip.
|
262
255
|
# The path can be local or remote for Sauce.
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
# The name to use for the test run on Sauce.
|
267
|
-
@app_name = opts.fetch :app_name, ENV['APP_NAME']
|
268
|
-
|
269
|
-
# Android app package
|
270
|
-
@app_package = opts.fetch :app_package, ENV['APP_PACKAGE']
|
271
|
-
|
272
|
-
# Android app starting activity.
|
273
|
-
@app_activity = opts.fetch :app_activity, ENV['APP_ACTIVITY']
|
274
|
-
|
275
|
-
# Android app waiting activity
|
276
|
-
@app_wait_activity = opts.fetch :app_wait_activity, ENV['APP_WAIT_ACTIVITY']
|
277
|
-
|
278
|
-
@android_coverage = opts.fetch :android_coverage, ENV['ANDROID_COVERAGE']
|
279
|
-
# init to empty hash because it's merged later as an optional desired cap.
|
280
|
-
@android_coverage = @android_coverage ? { androidCoverage: @android_coverage} : {}
|
281
|
-
|
282
|
-
# Sauce Username
|
283
|
-
@sauce_username = opts.fetch :sauce_username, ENV['SAUCE_USERNAME']
|
284
|
-
|
285
|
-
# Sauce Key
|
286
|
-
@sauce_access_key = opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY']
|
287
|
-
|
288
|
-
@port = opts.fetch :port, ENV['PORT'] || 4723
|
289
|
-
|
290
|
-
# 'iPhone Simulator'
|
291
|
-
# 'iPad Simulator'
|
292
|
-
# 'Android'
|
293
|
-
# 'Selendroid'
|
294
|
-
#
|
295
|
-
# :ios, :android, :selendroid
|
296
|
-
@device = opts.fetch :device, ENV['DEVICE']
|
297
|
-
raise 'Device must be set' unless @device
|
298
|
-
|
299
|
-
@device_type = opts.fetch :device_type, 'tablet'
|
300
|
-
@device_orientation = opts.fetch :device_orientation, 'portrait'
|
301
|
-
|
302
|
-
@full_reset = opts.fetch :full_reset, true
|
303
|
-
@no_reset = opts.fetch :no_reset, false
|
256
|
+
unless !@caps || @caps[:app].nil? || @caps[:app].empty?
|
257
|
+
@caps[:app] = self.class.absolute_app_path @caps[:app]
|
258
|
+
end
|
304
259
|
|
305
|
-
#
|
306
|
-
@
|
307
|
-
@
|
260
|
+
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
261
|
+
@device = @caps[:platformName]
|
262
|
+
@device = @device.is_a?(Symbol) ? @device : @device.downcase.strip.intern if @device
|
263
|
+
raise "platformName must be set. Not found in options: #{opts}" unless @device
|
264
|
+
raise 'platformName must be Android or iOS' unless [:android, :ios].include?(@device)
|
308
265
|
|
309
266
|
# load common methods
|
310
267
|
extend Appium::Common
|
311
|
-
if
|
268
|
+
if device_is_android?
|
312
269
|
# load Android specific methods
|
313
270
|
extend Appium::Android
|
314
271
|
else
|
@@ -321,7 +278,7 @@ module Appium
|
|
321
278
|
|
322
279
|
# enable debug patch
|
323
280
|
# !!'constant' == true
|
324
|
-
@debug =
|
281
|
+
@debug = appium_lib_opts.fetch :debug, !!defined?(Pry)
|
325
282
|
puts "Debug is: #{@debug}"
|
326
283
|
if @debug
|
327
284
|
ap opts unless opts.empty?
|
@@ -329,6 +286,7 @@ module Appium
|
|
329
286
|
patch_webdriver_bridge
|
330
287
|
end
|
331
288
|
|
289
|
+
|
332
290
|
# Save global reference to last created Appium driver for top level methods.
|
333
291
|
$driver = self
|
334
292
|
|
@@ -336,78 +294,58 @@ module Appium
|
|
336
294
|
# Subsequent drivers do not trigger promotion.
|
337
295
|
unless @@loaded
|
338
296
|
@@loaded = true
|
297
|
+
# load device methods exactly once
|
298
|
+
extend Appium::Device
|
299
|
+
|
339
300
|
# Promote only on Minitest::Spec (minitest 5) by default
|
340
301
|
Appium.promote_appium_methods ::Minitest::Spec
|
341
302
|
end
|
342
303
|
|
343
304
|
self # return newly created driver
|
344
|
-
end # def initialize
|
345
|
-
|
346
|
-
# Returns the status payload
|
347
|
-
#
|
348
|
-
# ```ruby
|
349
|
-
# {"status"=>0,
|
350
|
-
# "value"=>
|
351
|
-
# {"build"=>
|
352
|
-
# {"version"=>"0.8.2",
|
353
|
-
# "revision"=>"f2a2bc3782e4b0370d97a097d7e04913cf008995"}},
|
354
|
-
# "sessionId"=>"8f4b34a7-a9a9-4ac5-b125-36258143446a"}
|
355
|
-
# ```
|
356
|
-
#
|
357
|
-
# Discover the Appium rev running on the server.
|
358
|
-
#
|
359
|
-
# `status["value"]["build"]["revision"]`
|
360
|
-
# `f2a2bc3782e4b0370d97a097d7e04913cf008995`
|
361
|
-
#
|
362
|
-
# @return [JSON]
|
363
|
-
def status
|
364
|
-
driver.status.payload
|
365
|
-
end
|
366
|
-
|
367
|
-
# Returns the server's version string
|
368
|
-
# @return [String]
|
369
|
-
def server_version
|
370
|
-
status['value']['build']['version']
|
371
305
|
end
|
372
306
|
|
373
|
-
#
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
307
|
+
# Returns a hash of the driver attributes
|
308
|
+
def driver_attributes
|
309
|
+
attributes = { caps: @caps,
|
310
|
+
custom_url: @custom_url,
|
311
|
+
export_session: @export_session,
|
312
|
+
default_wait: @default_wait,
|
313
|
+
last_waits: @last_waits,
|
314
|
+
sauce_username: @sauce_username,
|
315
|
+
sauce_access_key: @sauce_access_key,
|
316
|
+
port: @port,
|
317
|
+
device: @device,
|
318
|
+
debug: @debug,
|
319
|
+
}
|
320
|
+
|
321
|
+
# Return duplicates so attributes are immutable
|
322
|
+
attributes.each do |key, value|
|
323
|
+
attributes[key] = value.duplicable? ? value.dup : value
|
324
|
+
end
|
325
|
+
attributes
|
390
326
|
end
|
391
327
|
|
392
|
-
|
393
|
-
|
394
|
-
def ios_capabilities
|
395
|
-
{
|
396
|
-
platform: 'OS X 10.9',
|
397
|
-
platformName: @device,
|
398
|
-
name: @app_name || 'Ruby Console iOS Appium',
|
399
|
-
:'device-orientation' => @device_orientation
|
400
|
-
}.merge(@raw_capabilities)
|
328
|
+
def device_is_android?
|
329
|
+
@device == :android
|
401
330
|
end
|
402
331
|
|
403
|
-
#
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
332
|
+
# Returns the server's version info
|
333
|
+
#
|
334
|
+
# ```ruby
|
335
|
+
# {
|
336
|
+
# "build" => {
|
337
|
+
# "version" => "0.18.1",
|
338
|
+
# "revision" => "d242ebcfd92046a974347ccc3a28f0e898595198"
|
339
|
+
# }
|
340
|
+
# }
|
341
|
+
# ```
|
342
|
+
#
|
343
|
+
# @return [Hash]
|
344
|
+
def appium_server_version
|
345
|
+
driver.remote_status
|
408
346
|
end
|
409
347
|
|
410
|
-
# Converts
|
348
|
+
# Converts app_path to an absolute path.
|
411
349
|
# @return [String] APP_PATH as an absolute path
|
412
350
|
def self.absolute_app_path app_path
|
413
351
|
raise 'APP_PATH not set!' if app_path.nil? || app_path.empty?
|
@@ -431,7 +369,7 @@ module Appium
|
|
431
369
|
app_path
|
432
370
|
end
|
433
371
|
|
434
|
-
# Get the server url
|
372
|
+
# Get the server url
|
435
373
|
# @return [String] the server url
|
436
374
|
def server_url
|
437
375
|
return @custom_url if @custom_url
|
@@ -469,40 +407,32 @@ module Appium
|
|
469
407
|
# Quits the driver
|
470
408
|
# @return [void]
|
471
409
|
def driver_quit
|
472
|
-
# rescue NoSuchDriverError
|
473
|
-
|
410
|
+
# rescue NoSuchDriverError or nil driver
|
411
|
+
@driver.quit rescue nil
|
474
412
|
end
|
475
413
|
|
476
414
|
# Creates a new global driver and quits the old one if it exists.
|
477
415
|
#
|
478
416
|
# @return [Selenium::WebDriver] the new global driver
|
479
417
|
def start_driver
|
480
|
-
@client
|
418
|
+
@client = @client || Selenium::WebDriver::Remote::Http::Default.new
|
481
419
|
@client.timeout = 999999
|
482
420
|
|
483
421
|
begin
|
484
|
-
@driver = Selenium::WebDriver.for :remote, http_client: @client, desired_capabilities:
|
485
|
-
# Load touch methods.
|
422
|
+
@driver = Selenium::WebDriver.for :remote, http_client: @client, desired_capabilities: @caps, url: server_url
|
423
|
+
# Load touch methods.
|
486
424
|
@driver.extend Selenium::WebDriver::DriverExtensions::HasTouchScreen
|
487
425
|
|
488
426
|
# export session
|
489
427
|
if @export_session
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
end
|
494
|
-
rescue
|
495
|
-
end
|
428
|
+
File.open('/tmp/appium_lib_session', 'w') do |f|
|
429
|
+
f.puts @driver.session_id
|
430
|
+
end rescue nil
|
496
431
|
end
|
497
432
|
rescue Errno::ECONNREFUSED
|
498
433
|
raise 'ERROR: Unable to connect to Appium. Is the server running?'
|
499
434
|
end
|
500
435
|
|
501
|
-
# Set timeout to a large number so that Appium doesn't quit
|
502
|
-
# when no commands are entered after 60 seconds.
|
503
|
-
# broken on selendroid: https://github.com/appium/appium/issues/513
|
504
|
-
mobile :setCommandTimeout, timeout: 9999 unless @device == 'Selendroid'
|
505
|
-
|
506
436
|
# Set implicit wait by default unless we're using Pry.
|
507
437
|
@driver.manage.timeouts.implicit_wait = @default_wait unless defined? Pry
|
508
438
|
|
@@ -511,8 +441,8 @@ module Appium
|
|
511
441
|
|
512
442
|
# Set implicit wait and default_wait to zero.
|
513
443
|
def no_wait
|
514
|
-
@last_waits
|
515
|
-
@default_wait
|
444
|
+
@last_waits = [@default_wait, 0]
|
445
|
+
@default_wait = 0
|
516
446
|
@driver.manage.timeouts.implicit_wait = 0
|
517
447
|
end
|
518
448
|
|
@@ -537,7 +467,7 @@ module Appium
|
|
537
467
|
else
|
538
468
|
@default_wait = timeout
|
539
469
|
# puts "last waits before: #{@last_waits}"
|
540
|
-
@last_waits
|
470
|
+
@last_waits = [@last_waits.last, @default_wait]
|
541
471
|
# puts "last waits after: #{@last_waits}"
|
542
472
|
end
|
543
473
|
|
@@ -569,7 +499,7 @@ module Appium
|
|
569
499
|
# which then gets converted to a 1 second wait.
|
570
500
|
@driver.manage.timeouts.implicit_wait = pre_check
|
571
501
|
# the element exists unless an error is raised.
|
572
|
-
exists
|
502
|
+
exists = true
|
573
503
|
|
574
504
|
begin
|
575
505
|
search_block.call # search for element
|
@@ -591,25 +521,6 @@ module Appium
|
|
591
521
|
@driver.execute_script script, *args
|
592
522
|
end
|
593
523
|
|
594
|
-
# Helper method for mobile gestures
|
595
|
-
#
|
596
|
-
# https://github.com/appium/appium/wiki/Automating-mobile-gestures
|
597
|
-
#
|
598
|
-
# driver.execute_script 'mobile: swipe', endX: 100, endY: 100, duration: 0.01
|
599
|
-
#
|
600
|
-
# becomes
|
601
|
-
#
|
602
|
-
# mobile :swipe, endX: 100, endY: 100, duration: 0.01
|
603
|
-
# @param method [String, Symbol] the method to execute
|
604
|
-
# @param args [*args] the args to pass to the method
|
605
|
-
# @return [Object]
|
606
|
-
def mobile method, *args
|
607
|
-
raise 'Method must not be nil' if method.nil?
|
608
|
-
raise 'Method must have .to_s' unless method.respond_to? :to_s
|
609
|
-
|
610
|
-
@driver.execute_script "mobile: #{method.to_s}", *args
|
611
|
-
end
|
612
|
-
|
613
524
|
# Calls @driver.find_elements
|
614
525
|
#
|
615
526
|
# @param args [*args] the args to use
|
@@ -633,10 +544,10 @@ module Appium
|
|
633
544
|
driver_quit
|
634
545
|
exit # exit pry
|
635
546
|
end
|
636
|
-
end #
|
637
|
-
end #
|
547
|
+
end # class Driver
|
548
|
+
end # module Appium
|
638
549
|
|
639
550
|
# Paging in Pry is annoying :q required to exit.
|
640
551
|
# With pager disabled, the output is similar to IRB
|
641
552
|
# Only set if Pry is defined.
|
642
|
-
Pry.config.pager = false if defined?(Pry)
|
553
|
+
Pry.config.pager = false if defined?(Pry)
|