appium_lib 0.24.1 → 1.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 +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)
|