appium_lib_core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +202 -0
- data/README.md +5 -0
- data/Rakefile +62 -0
- data/Thorfile +7 -0
- data/appium_lib_core.gemspec +35 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/appium_lib_core.rb +41 -0
- data/lib/appium_lib_core/android.rb +5 -0
- data/lib/appium_lib_core/android/device.rb +174 -0
- data/lib/appium_lib_core/android/espresso/bridge.rb +16 -0
- data/lib/appium_lib_core/android/search_context.rb +18 -0
- data/lib/appium_lib_core/android/touch.rb +17 -0
- data/lib/appium_lib_core/android/uiautomator1/bridge.rb +16 -0
- data/lib/appium_lib_core/android/uiautomator2/bridge.rb +16 -0
- data/lib/appium_lib_core/android_espresso.rb +5 -0
- data/lib/appium_lib_core/android_uiautomator2.rb +5 -0
- data/lib/appium_lib_core/common.rb +6 -0
- data/lib/appium_lib_core/common/base.rb +8 -0
- data/lib/appium_lib_core/common/base/bridge.rb +47 -0
- data/lib/appium_lib_core/common/base/capabilities.rb +16 -0
- data/lib/appium_lib_core/common/base/command.rb +10 -0
- data/lib/appium_lib_core/common/base/driver.rb +51 -0
- data/lib/appium_lib_core/common/base/http_default.rb +13 -0
- data/lib/appium_lib_core/common/base/search_context.rb +89 -0
- data/lib/appium_lib_core/common/base/wait.rb +56 -0
- data/lib/appium_lib_core/common/command.rb +77 -0
- data/lib/appium_lib_core/common/device.rb +567 -0
- data/lib/appium_lib_core/common/error.rb +18 -0
- data/lib/appium_lib_core/common/log.rb +30 -0
- data/lib/appium_lib_core/common/logger.rb +30 -0
- data/lib/appium_lib_core/device/multi_touch.rb +48 -0
- data/lib/appium_lib_core/device/touch_actions.rb +191 -0
- data/lib/appium_lib_core/driver.rb +459 -0
- data/lib/appium_lib_core/ios.rb +7 -0
- data/lib/appium_lib_core/ios/device.rb +54 -0
- data/lib/appium_lib_core/ios/search_context.rb +27 -0
- data/lib/appium_lib_core/ios/touch.rb +17 -0
- data/lib/appium_lib_core/ios/uiautomation/bridge.rb +18 -0
- data/lib/appium_lib_core/ios/uiautomation/patch.rb +20 -0
- data/lib/appium_lib_core/ios/xcuitest/bridge.rb +18 -0
- data/lib/appium_lib_core/ios/xcuitest/device.rb +66 -0
- data/lib/appium_lib_core/ios/xcuitest/search_context.rb +40 -0
- data/lib/appium_lib_core/ios_xcuitest.rb +8 -0
- data/lib/appium_lib_core/patch.rb +78 -0
- data/lib/appium_lib_core/version.rb +6 -0
- data/release_notes.md +0 -0
- metadata +262 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Appium
|
2
|
+
module Core
|
3
|
+
class Base
|
4
|
+
module SearchContext
|
5
|
+
# referenced: ::Selenium::WebDriver::SearchContext
|
6
|
+
|
7
|
+
FINDERS = ::Selenium::WebDriver::SearchContext::FINDERS.merge(accessibility_id: 'accessibility id')
|
8
|
+
|
9
|
+
def self.add_finders(finders)
|
10
|
+
FINDERS.merge!(finders)
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Find the first element matching the given arguments
|
15
|
+
#
|
16
|
+
# @overload find_element(how, what)
|
17
|
+
# @param [Symbol, String] how The method to find the element by
|
18
|
+
# @param [String] what The locator to use
|
19
|
+
# @overload find_element(opts)
|
20
|
+
# @param [Hash] opts Find options
|
21
|
+
# @option opts [Symbol] :how Key named after the method to find the element by, containing the locator
|
22
|
+
# @return [Element]
|
23
|
+
#
|
24
|
+
# @raise [Error::NoSuchElementError] if the element doesn't exist
|
25
|
+
#
|
26
|
+
# @example Find element with accessibility id
|
27
|
+
# find_elements :accessibility_id, 'Animation'
|
28
|
+
# find_elements :accessibility_id, 'Animation'
|
29
|
+
#
|
30
|
+
def find_element(*args)
|
31
|
+
how, what = extract_args(args)
|
32
|
+
by = _set_by_from_finders(how)
|
33
|
+
begin
|
34
|
+
bridge.find_element_by by, what.to_s, ref
|
35
|
+
rescue Selenium::WebDriver::Error::TimeOutError
|
36
|
+
raise Selenium::WebDriver::Error::NoSuchElementError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Find all elements matching the given arguments
|
42
|
+
#
|
43
|
+
# @see SearchContext#find_element
|
44
|
+
#
|
45
|
+
def find_elements(*args)
|
46
|
+
how, what = extract_args(args)
|
47
|
+
by = _set_by_from_finders(how)
|
48
|
+
begin
|
49
|
+
bridge.find_elements_by by, what.to_s, ref
|
50
|
+
rescue Selenium::WebDriver::Error::TimeOutError
|
51
|
+
raise Selenium::WebDriver::Error::NoSuchElementError
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def _set_by_from_finders(how)
|
58
|
+
finders = FINDERS
|
59
|
+
by = finders[how.to_sym]
|
60
|
+
raise ArgumentError, "cannot find element by #{how.inspect}" unless by
|
61
|
+
by
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract_args(args)
|
65
|
+
case args.size
|
66
|
+
when 2
|
67
|
+
args
|
68
|
+
when 1
|
69
|
+
arg = args.first
|
70
|
+
|
71
|
+
unless arg.respond_to?(:shift)
|
72
|
+
raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
|
73
|
+
end
|
74
|
+
|
75
|
+
# this will be a single-entry hash, so use #shift over #first or #[]
|
76
|
+
arr = arg.dup.shift
|
77
|
+
unless arr.size == 2
|
78
|
+
raise ArgumentError, "expected #{arr.inspect} to have 2 elements"
|
79
|
+
end
|
80
|
+
|
81
|
+
arr
|
82
|
+
else
|
83
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end # module SearchContext
|
87
|
+
end # class Base
|
88
|
+
end # module Core
|
89
|
+
end # module Appium
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# rubocop:disable Lint/HandleExceptions
|
2
|
+
module Appium
|
3
|
+
module Core
|
4
|
+
class Base
|
5
|
+
class Wait < ::Selenium::WebDriver::Wait
|
6
|
+
require 'timeout' # for wait
|
7
|
+
|
8
|
+
def initialize(opts = {})
|
9
|
+
valid_keys = [:timeout, :interval, :message, :ignore, :return_if_true]
|
10
|
+
invalid_keys = []
|
11
|
+
opts.keys.each { |key| invalid_keys << key unless valid_keys.include?(key) }
|
12
|
+
# [:one, :two] => :one, :two
|
13
|
+
unless invalid_keys.empty?
|
14
|
+
raise "Invalid keys #{invalid_keys.to_s[1..-2]}. Valid keys are #{valid_keys.to_s[1..-2]}"
|
15
|
+
end
|
16
|
+
|
17
|
+
@timeout = opts.fetch(:timeout, DEFAULT_TIMEOUT)
|
18
|
+
@interval = opts.fetch(:interval, DEFAULT_INTERVAL)
|
19
|
+
@message = opts[:message]
|
20
|
+
@ignored = Array(opts[:ignore] || ::Exception)
|
21
|
+
@return_if_true = opts[:return_if_true]
|
22
|
+
|
23
|
+
super(timeout: @timeout, interval: @interval, message: @message, ignore: @ignored)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Wait code from the selenium Ruby gem
|
27
|
+
# https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
|
28
|
+
def until
|
29
|
+
end_time = Time.now + @timeout
|
30
|
+
last_error = nil
|
31
|
+
|
32
|
+
until Time.now > end_time
|
33
|
+
begin
|
34
|
+
return yield unless @return_if_true
|
35
|
+
|
36
|
+
result = yield
|
37
|
+
return result if result
|
38
|
+
rescue ::Errno::ECONNREFUSED => e
|
39
|
+
raise e
|
40
|
+
rescue *@ignored => last_error
|
41
|
+
# swallowed
|
42
|
+
end
|
43
|
+
|
44
|
+
sleep @interval
|
45
|
+
end
|
46
|
+
|
47
|
+
msg = @message ? @message.dup : "timed out after #{@timeout} seconds"
|
48
|
+
|
49
|
+
msg << " (#{last_error.message})" if last_error
|
50
|
+
|
51
|
+
raise Selenium::WebDriver::Error::TimeOutError, msg
|
52
|
+
end
|
53
|
+
end # module Wait
|
54
|
+
end # module Base
|
55
|
+
end # module Core
|
56
|
+
end # module Appium
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative 'base/command'
|
2
|
+
|
3
|
+
module Appium
|
4
|
+
module Core
|
5
|
+
# ref: https://github.com/appium/appium-base-driver/blob/master/lib/mjsonwp/routes.js
|
6
|
+
module Commands
|
7
|
+
COMMAND_NO_ARG = {
|
8
|
+
# Common
|
9
|
+
shake: [:post, 'session/:session_id/appium/device/shake'.freeze],
|
10
|
+
launch_app: [:post, 'session/:session_id/appium/app/launch'.freeze],
|
11
|
+
close_app: [:post, 'session/:session_id/appium/app/close'.freeze],
|
12
|
+
reset: [:post, 'session/:session_id/appium/app/reset'.freeze],
|
13
|
+
device_locked?: [:post, 'session/:session_id/appium/device/is_locked'.freeze],
|
14
|
+
unlock: [:post, 'session/:session_id/appium/device/unlock'.freeze],
|
15
|
+
device_time: [:get, 'session/:session_id/appium/device/system_time'.freeze],
|
16
|
+
current_context: [:get, 'session/:session_id/context'.freeze],
|
17
|
+
|
18
|
+
# Android
|
19
|
+
open_notifications: [:post, 'session/:session_id/appium/device/open_notifications'.freeze],
|
20
|
+
toggle_airplane_mode: [:post, 'session/:session_id/appium/device/toggle_airplane_mode'.freeze],
|
21
|
+
current_activity: [:get, 'session/:session_id/appium/device/current_activity'.freeze],
|
22
|
+
current_package: [:get, 'session/:session_id/appium/device/current_package'.freeze],
|
23
|
+
get_system_bars: [:get, 'session/:session_id/appium/device/system_bars'.freeze],
|
24
|
+
get_display_density: [:get, 'session/:session_id/appium/device/display_density'.freeze],
|
25
|
+
is_keyboard_shown: [:get, 'session/:session_id/appium/device/is_keyboard_shown'.freeze],
|
26
|
+
get_network_connection: [:get, 'session/:session_id/network_connection'.freeze],
|
27
|
+
get_performance_data_types: [:post, 'session/:session_id/appium/performanceData/types'.freeze],
|
28
|
+
stop_recording_screen: [:post, 'session/:session_id/appium/stop_recording_screen'.freeze]
|
29
|
+
# iOS
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
# Some commands differ for each driver.
|
33
|
+
COMMAND = {
|
34
|
+
# common
|
35
|
+
available_contexts: [:get, 'session/:session_id/contexts'.freeze],
|
36
|
+
set_context: [:post, 'session/:session_id/context'.freeze],
|
37
|
+
app_strings: [:post, 'session/:session_id/appium/app/strings'.freeze],
|
38
|
+
lock: [:post, 'session/:session_id/appium/device/lock'.freeze],
|
39
|
+
install_app: [:post, 'session/:session_id/appium/device/install_app'.freeze],
|
40
|
+
remove_app: [:post, 'session/:session_id/appium/device/remove_app'.freeze],
|
41
|
+
app_installed?: [:post, 'session/:session_id/appium/device/app_installed'.freeze],
|
42
|
+
background_app: [:post, 'session/:session_id/appium/app/background'.freeze],
|
43
|
+
hide_keyboard: [:post, 'session/:session_id/appium/device/hide_keyboard'.freeze],
|
44
|
+
press_keycode: [:post, 'session/:session_id/appium/device/press_keycode'.freeze],
|
45
|
+
long_press_keycode: [:post, 'session/:session_id/appium/device/long_press_keycode'.freeze],
|
46
|
+
set_immediate_value: [:post, 'session/:session_id/appium/element/:id/value'.freeze],
|
47
|
+
replace_value: [:post, 'session/:session_id/appium/element/:id/replace_value'.freeze],
|
48
|
+
push_file: [:post, 'session/:session_id/appium/device/push_file'.freeze],
|
49
|
+
pull_file: [:post, 'session/:session_id/appium/device/pull_file'.freeze],
|
50
|
+
pull_folder: [:post, 'session/:session_id/appium/device/pull_folder'.freeze],
|
51
|
+
get_settings: [:get, 'session/:session_id/appium/settings'.freeze],
|
52
|
+
update_settings: [:post, 'session/:session_id/appium/settings'.freeze],
|
53
|
+
touch_actions: [:post, 'session/:session_id/touch/perform'.freeze],
|
54
|
+
multi_touch: [:post, 'session/:session_id/touch/multi/perform'.freeze]
|
55
|
+
}.freeze
|
56
|
+
|
57
|
+
COMMAND_ANDROID = {
|
58
|
+
start_activity: [:post, 'session/:session_id/appium/device/start_activity'.freeze],
|
59
|
+
end_coverage: [:post, 'session/:session_id/appium/app/end_test_coverage'.freeze],
|
60
|
+
set_network_connection: [:post, 'session/:session_id/network_connection'.freeze],
|
61
|
+
get_performance_data: [:post, 'session/:session_id/appium/getPerformanceData'.freeze],
|
62
|
+
start_recording_screen: [:post, 'session/:session_id/appium/start_recording_screen'.freeze]
|
63
|
+
}.freeze
|
64
|
+
|
65
|
+
COMMAND_IOS = {
|
66
|
+
touch_id: [:post, 'session/:session_id/appium/simulator/touch_id'.freeze],
|
67
|
+
toggle_touch_id_enrollment: [:post, 'session/:session_id/appium/simulator/toggle_touch_id_enrollment'.freeze]
|
68
|
+
}.freeze
|
69
|
+
|
70
|
+
COMMANDS = {}.merge(COMMAND).merge(COMMAND_ANDROID).merge(COMMAND_IOS)
|
71
|
+
.merge(COMMAND_NO_ARG).freeze
|
72
|
+
|
73
|
+
COMMANDS_EXTEND_OSS = COMMANDS.merge(::Appium::Core::Base::Commands::OSS).freeze
|
74
|
+
COMMANDS_EXTEND_W3C = COMMANDS.merge(::Appium::Core::Base::Commands::W3C).freeze
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,567 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Appium
|
4
|
+
module Core
|
5
|
+
module Device
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
####
|
9
|
+
## No argument
|
10
|
+
####
|
11
|
+
|
12
|
+
# @!method current_activity
|
13
|
+
# Get current activity name
|
14
|
+
# @return [String] An activity name
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
#
|
18
|
+
# @driver.current_activity # '.ApiDemos'
|
19
|
+
#
|
20
|
+
|
21
|
+
# @!method current_package
|
22
|
+
# Get current package name
|
23
|
+
# @return [String] A package name
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
#
|
27
|
+
# @driver.current_package # 'com.example.android.apis'
|
28
|
+
#
|
29
|
+
|
30
|
+
# @!method get_system_bars
|
31
|
+
# Get system bar's information
|
32
|
+
# @return [String]
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
#
|
36
|
+
# @driver.get_system_bars
|
37
|
+
#
|
38
|
+
|
39
|
+
# @!method get_display_density
|
40
|
+
# Get connected device's density.
|
41
|
+
# @return [Integer] The size of density
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
#
|
45
|
+
# @driver.get_display_density # 320
|
46
|
+
#
|
47
|
+
|
48
|
+
# @!method is_keyboard_shown
|
49
|
+
# Get whether keyboard is displayed or not.
|
50
|
+
# @return [Bool] Return true if keyboard is shown. Return false if keyboard is hidden.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# @driver.is_keyboard_shown # false
|
54
|
+
#
|
55
|
+
|
56
|
+
# @!method launch_app
|
57
|
+
# Start the simulator and application configured with desired capabilities
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
#
|
61
|
+
# @driver.launch_app
|
62
|
+
#
|
63
|
+
|
64
|
+
# @!method reset
|
65
|
+
# Reset the device, relaunching the application.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
#
|
69
|
+
# @driver.reset
|
70
|
+
#
|
71
|
+
|
72
|
+
# @!method shake
|
73
|
+
# Cause the device to shake
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
#
|
77
|
+
# @driver.shake
|
78
|
+
#
|
79
|
+
|
80
|
+
# @!method toggle_flight_mode
|
81
|
+
# Toggle flight mode on or off
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
#
|
85
|
+
# @driver.toggle_flight_mode
|
86
|
+
#
|
87
|
+
|
88
|
+
# @!method device_locked?
|
89
|
+
# Check current device status is weather locked or not
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
#
|
93
|
+
# @driver.device_locked?
|
94
|
+
#
|
95
|
+
|
96
|
+
# @!method get_network_connection
|
97
|
+
# Get the device network connection current status
|
98
|
+
# See set_network_connection method for return value
|
99
|
+
|
100
|
+
####
|
101
|
+
## With arguments
|
102
|
+
####
|
103
|
+
|
104
|
+
# @!method app_strings(language = nil)
|
105
|
+
# Return the hash of all localization strings.
|
106
|
+
# @return [Hash]
|
107
|
+
#
|
108
|
+
# @example
|
109
|
+
#
|
110
|
+
# @driver.app_strings #=> "TransitionsTitle"=>"Transitions", "WebTitle"=>"Web"
|
111
|
+
#
|
112
|
+
|
113
|
+
# @!method background_app(duration = 0)
|
114
|
+
# Backgrounds the app for a set number of seconds.
|
115
|
+
# This is a blocking application
|
116
|
+
# @param [Integer] duration How many seconds to background the app for.
|
117
|
+
# @return [String]
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
#
|
121
|
+
# @driver.background_app
|
122
|
+
# @driver.background_app(5)
|
123
|
+
# @driver.background_app(-1) #=> the app never come back. https://github.com/appium/appium/issues/7741
|
124
|
+
#
|
125
|
+
|
126
|
+
# @!method hide_keyboard(close_key = nil, strategy = nil)
|
127
|
+
# Hide the onscreen keyboard
|
128
|
+
# @param [String] close_key The name of the key which closes the keyboard.
|
129
|
+
# Defaults to 'Done' for iOS(except for XCUITest).
|
130
|
+
# @param [Symbol] strategy The symbol of the strategy which closes the keyboard.
|
131
|
+
# XCUITest ignore this argument.
|
132
|
+
# Default for iOS is `:pressKey`. Default for Android is `:tapOutside`.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
#
|
136
|
+
# @driver.hide_keyboard # Close a keyboard with the 'Done' button
|
137
|
+
# @driver.hide_keyboard('Finished') # Close a keyboard with the 'Finished' button
|
138
|
+
# @driver.hide_keyboard(nil, :tapOutside) # Close a keyboard with tapping out side of keyboard
|
139
|
+
#
|
140
|
+
|
141
|
+
# @!method press_keycode(key, metastate = nil)
|
142
|
+
# Press keycode on the device.
|
143
|
+
# http://developer.android.com/reference/android/view/KeyEvent.html
|
144
|
+
# @param [integer] key The key to press.
|
145
|
+
# @param [String] metastate The state the metakeys should be in when pressing the key.
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
#
|
149
|
+
# @driver.press_keycode 82
|
150
|
+
#
|
151
|
+
|
152
|
+
# @!method long_press_keycode(key, metastate = nil)
|
153
|
+
# Long press keycode on the device.
|
154
|
+
# http://developer.android.com/reference/android/view/KeyEvent.html
|
155
|
+
# @param [integer] key The key to long press.
|
156
|
+
# @param [String] metastate The state the metakeys should be in when long pressing the key.
|
157
|
+
#
|
158
|
+
# @example
|
159
|
+
#
|
160
|
+
# @driver.long_press_keycode 82
|
161
|
+
#
|
162
|
+
|
163
|
+
# @!method push_file(path, filedata)
|
164
|
+
# Place a file in a specific location on the device.
|
165
|
+
# @param [String] path The absolute path on the device to store data at.
|
166
|
+
# @param [String] filedata Raw file data to be sent to the device. Converted to base64 in the method.
|
167
|
+
#
|
168
|
+
# @example
|
169
|
+
#
|
170
|
+
# @driver.push_file "/file/to/path", data
|
171
|
+
#
|
172
|
+
|
173
|
+
# @!method pull_file(path)
|
174
|
+
# Retrieve a file from the device. This can retrieve an absolute path or
|
175
|
+
# a path relative to the installed app (iOS only).
|
176
|
+
# @param [String] path Either an absolute path OR, for iOS devices, a path relative to the app, as described.
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
#
|
180
|
+
# @driver.pull_file '/local/data/some/path' #=> Get the file at that path
|
181
|
+
# @driver.pull_file 'Shenanigans.app/some/file' #=> Get 'some/file' from the install location of Shenanigans.app
|
182
|
+
#
|
183
|
+
|
184
|
+
# @!method pull_folder(path)
|
185
|
+
# Retrieve a folder from the device.
|
186
|
+
# @param [String] path absolute path to the folder
|
187
|
+
#
|
188
|
+
# @example
|
189
|
+
#
|
190
|
+
# @driver.pull_folder '/data/local/tmp' #=> Get the folder at that path
|
191
|
+
#
|
192
|
+
|
193
|
+
# @!method get_settings
|
194
|
+
# Get appium Settings for current test session
|
195
|
+
#
|
196
|
+
# @example
|
197
|
+
#
|
198
|
+
# @driver.pull_folder '/data/local/tmp' #=> Get the folder at that path
|
199
|
+
#
|
200
|
+
|
201
|
+
# @!method update_settings(settings)
|
202
|
+
# Update Appium Settings for current test session
|
203
|
+
# @param [Hash] settings Settings to update, keys are settings, values to value to set each setting to
|
204
|
+
#
|
205
|
+
# @example
|
206
|
+
#
|
207
|
+
# @driver.update_settings('allowInvisibleElements': true)
|
208
|
+
#
|
209
|
+
|
210
|
+
# @!method set_immediate_value(element, *value)
|
211
|
+
# Set the value to element directly
|
212
|
+
#
|
213
|
+
# @example
|
214
|
+
#
|
215
|
+
# set_immediate_value element, 'hello'
|
216
|
+
#
|
217
|
+
|
218
|
+
# @!method replace_value(element, *value)
|
219
|
+
# Replace the value to element directly
|
220
|
+
#
|
221
|
+
# @example
|
222
|
+
#
|
223
|
+
# replace_value element, 'hello'
|
224
|
+
#
|
225
|
+
|
226
|
+
# @!method ime_activate(ime_name)
|
227
|
+
# Android only. Make an engine that is available active.
|
228
|
+
# @param [String] ime_name The IME owning the activity [required]
|
229
|
+
#
|
230
|
+
# @example
|
231
|
+
#
|
232
|
+
# ime_activate engine: 'com.android.inputmethod.latin/.LatinIME'
|
233
|
+
#
|
234
|
+
|
235
|
+
# @!method ime_available_engines
|
236
|
+
# Android only. List all available input engines on the machine.
|
237
|
+
#
|
238
|
+
# @example
|
239
|
+
#
|
240
|
+
# ime_available_engines #=> Get the list of IME installed in the target device
|
241
|
+
#
|
242
|
+
|
243
|
+
# @!method ime_active_engine
|
244
|
+
# Android only. Get the name of the active IME engine.
|
245
|
+
#
|
246
|
+
# @example
|
247
|
+
#
|
248
|
+
# ime_active_engine #=> Get the current active IME such as 'com.android.inputmethod.latin/.LatinIME'
|
249
|
+
#
|
250
|
+
|
251
|
+
# @!method ime_activated
|
252
|
+
# Android only. Indicates whether IME input is active at the moment (not if it is available).
|
253
|
+
#
|
254
|
+
# @example
|
255
|
+
#
|
256
|
+
# ime_activated #=> True if IME is activated
|
257
|
+
#
|
258
|
+
|
259
|
+
# @!method ime_deactivate
|
260
|
+
# Android only. De-activates the currently-active IME engine.
|
261
|
+
#
|
262
|
+
# @example
|
263
|
+
#
|
264
|
+
# ime_deactivate #=> Deactivate current IME engine
|
265
|
+
#
|
266
|
+
|
267
|
+
####
|
268
|
+
## class << self
|
269
|
+
####
|
270
|
+
|
271
|
+
class << self
|
272
|
+
def extended(_mod)
|
273
|
+
extend_webdriver_with_forwardable
|
274
|
+
|
275
|
+
::Appium::Core::Commands::COMMAND_NO_ARG.each_key do |method|
|
276
|
+
add_endpoint_method method
|
277
|
+
end
|
278
|
+
|
279
|
+
# call http://www.rubydoc.info/gems/selenium-webdriver/Selenium/WebDriver/DriverExtensions/HasRemoteStatus#remote_status-instance_method
|
280
|
+
add_endpoint_method(:remote_status) do
|
281
|
+
def remote_status
|
282
|
+
execute(:status, {}) || []
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# TODO: Don't define selenium-side methods. We pick up from them.
|
287
|
+
# ::Appium::Core::Base::Commands::OSS.each_key do |method|
|
288
|
+
# add_endpoint_method method
|
289
|
+
# end
|
290
|
+
|
291
|
+
add_endpoint_method(:available_contexts) do
|
292
|
+
def available_contexts
|
293
|
+
# return empty array instead of nil on failure
|
294
|
+
execute(:available_contexts, {}) || []
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
add_endpoint_method(:app_strings) do
|
299
|
+
def app_strings(language = nil)
|
300
|
+
opts = language ? { language: language } : {}
|
301
|
+
execute :app_strings, {}, opts
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
add_endpoint_method(:lock) do
|
306
|
+
def lock(duration)
|
307
|
+
execute :lock, {}, seconds: duration
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
add_endpoint_method(:install_app) do
|
312
|
+
def install_app(path)
|
313
|
+
execute :install_app, {}, appPath: path
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
add_endpoint_method(:remove_app) do
|
318
|
+
def remove_app(id)
|
319
|
+
execute :remove_app, {}, appId: id
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
add_endpoint_method(:app_installed?) do
|
324
|
+
def app_installed?(app_id)
|
325
|
+
execute :app_installed?, {}, bundleId: app_id
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
add_endpoint_method(:background_app) do
|
330
|
+
def background_app(duration = 0)
|
331
|
+
execute :background_app, {}, seconds: duration
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
add_endpoint_method(:set_context) do
|
336
|
+
def set_context(context = null)
|
337
|
+
execute :set_context, {}, name: context
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
add_endpoint_method(:hide_keyboard) do
|
342
|
+
def hide_keyboard(close_key = nil, strategy = nil)
|
343
|
+
option = {}
|
344
|
+
|
345
|
+
option[:key] = close_key || 'Done' # default to Done key.
|
346
|
+
option[:strategy] = strategy || :pressKey # default to pressKey
|
347
|
+
|
348
|
+
execute :hide_keyboard, {}, option
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
add_endpoint_method(:press_keycode) do
|
353
|
+
def press_keycode(key, metastate = nil)
|
354
|
+
args = { keycode: key }
|
355
|
+
args[:metastate] = metastate if metastate
|
356
|
+
execute :press_keycode, {}, args
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
add_endpoint_method(:long_press_keycode) do
|
361
|
+
def long_press_keycode(key, metastate = nil)
|
362
|
+
args = { keycode: key }
|
363
|
+
args[:metastate] = metastate if metastate
|
364
|
+
execute :long_press_keycode, {}, args
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
add_endpoint_method(:set_immediate_value) do
|
369
|
+
def set_immediate_value(element, *value)
|
370
|
+
keys = ::Selenium::WebDriver::Keys.encode(value)
|
371
|
+
execute :set_immediate_value, { id: element.ref }, value: Array(keys)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
add_endpoint_method(:replace_value) do
|
376
|
+
def replace_value(element, *value)
|
377
|
+
keys = ::Selenium::WebDriver::Keys.encode(value)
|
378
|
+
execute :replace_value, { id: element.ref }, value: Array(keys)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
add_endpoint_method(:push_file) do
|
383
|
+
def push_file(path, filedata)
|
384
|
+
encoded_data = Base64.encode64 filedata
|
385
|
+
execute :push_file, {}, path: path, data: encoded_data
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
add_endpoint_method(:pull_file) do
|
390
|
+
def pull_file(path)
|
391
|
+
data = execute :pull_file, {}, path: path
|
392
|
+
Base64.decode64 data
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
add_endpoint_method(:pull_folder) do
|
397
|
+
def pull_folder(path)
|
398
|
+
data = execute :pull_folder, {}, path: path
|
399
|
+
Base64.decode64 data
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
add_endpoint_method(:get_settings) do
|
404
|
+
def get_settings
|
405
|
+
execute :get_settings, {}
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
add_endpoint_method(:update_settings) do
|
410
|
+
def update_settings(settings)
|
411
|
+
execute :update_settings, {}, settings: settings
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
add_touch_actions
|
416
|
+
add_ime_actions
|
417
|
+
end
|
418
|
+
|
419
|
+
# def extended
|
420
|
+
|
421
|
+
# @private
|
422
|
+
def add_endpoint_method(method)
|
423
|
+
block_given? ? create_bridge_command(method, &Proc.new) : create_bridge_command(method)
|
424
|
+
|
425
|
+
delegate_driver_method method
|
426
|
+
delegate_from_appium_driver method
|
427
|
+
end
|
428
|
+
|
429
|
+
# @private CoreBridge
|
430
|
+
def extend_webdriver_with_forwardable
|
431
|
+
return if ::Appium::Core::Base::Driver.is_a? Forwardable
|
432
|
+
::Appium::Core::Base::Driver.class_eval do
|
433
|
+
extend Forwardable
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
# @private
|
438
|
+
def delegate_driver_method(method)
|
439
|
+
return if ::Appium::Core::Base::Driver.method_defined? method
|
440
|
+
::Appium::Core::Base::Driver.class_eval { def_delegator :@bridge, method }
|
441
|
+
end
|
442
|
+
|
443
|
+
# @private
|
444
|
+
def delegate_from_appium_driver(method, delegation_target = :driver)
|
445
|
+
def_delegator delegation_target, method
|
446
|
+
end
|
447
|
+
|
448
|
+
# @private
|
449
|
+
def create_bridge_command(method)
|
450
|
+
::Appium::Core::Base::CoreBridgeOSS.class_eval do
|
451
|
+
block_given? ? class_eval(&Proc.new) : define_method(method) { execute method }
|
452
|
+
end
|
453
|
+
::Appium::Core::Base::CoreBridgeW3C.class_eval do
|
454
|
+
block_given? ? class_eval(&Proc.new) : define_method(method) { execute method }
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def add_touch_actions
|
459
|
+
add_endpoint_method(:touch_actions) do
|
460
|
+
def touch_actions(actions)
|
461
|
+
actions = { actions: [actions].flatten }
|
462
|
+
execute :touch_actions, {}, actions
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
add_endpoint_method(:multi_touch) do
|
467
|
+
def multi_touch(actions)
|
468
|
+
execute :multi_touch, {}, actions: actions
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
def add_ime_actions
|
474
|
+
add_endpoint_method(:ime_activate) do
|
475
|
+
def ime_activate(ime_name)
|
476
|
+
# from Selenium::WebDriver::Remote::OSS
|
477
|
+
execute :ime_activate_engine, {}, engine: ime_name
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
add_endpoint_method(:ime_available_engines) do
|
482
|
+
def ime_available_engines
|
483
|
+
execute :ime_get_available_engines
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
add_endpoint_method(:ime_active_engine) do
|
488
|
+
# from Selenium::WebDriver::Remote::OSS
|
489
|
+
def ime_active_engine
|
490
|
+
execute :ime_get_active_engine
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
add_endpoint_method(:ime_activated) do
|
495
|
+
# from Selenium::WebDriver::Remote::OSS
|
496
|
+
def ime_activated
|
497
|
+
execute :ime_is_activated
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
add_endpoint_method(:ime_deactivate) do
|
502
|
+
# from Selenium::WebDriver::Remote::OSS
|
503
|
+
def ime_deactivate
|
504
|
+
execute :ime_deactivate, {}
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end # class << self
|
509
|
+
|
510
|
+
# @!method set_context(context)
|
511
|
+
# Change the context to the given context.
|
512
|
+
# @param [String] context The context to change to
|
513
|
+
#
|
514
|
+
# @example
|
515
|
+
#
|
516
|
+
# @driver.set_context "NATIVE_APP"
|
517
|
+
#
|
518
|
+
|
519
|
+
# @!method current_context
|
520
|
+
# @return [String] The context currently being used.
|
521
|
+
#
|
522
|
+
# @example
|
523
|
+
#
|
524
|
+
# @driver.current_context
|
525
|
+
#
|
526
|
+
|
527
|
+
# @!method available_contexts
|
528
|
+
# @return [Array<String>] All usable contexts, as an array of strings.
|
529
|
+
#
|
530
|
+
# @example
|
531
|
+
#
|
532
|
+
# @driver.available_contexts
|
533
|
+
#
|
534
|
+
|
535
|
+
# Perform a block within the given context, then switch back to the starting context.
|
536
|
+
# @param [String] context The context to switch to for the duration of the block.
|
537
|
+
#
|
538
|
+
# @example
|
539
|
+
#
|
540
|
+
# result = @driver.within_context('NATIVE_APP') do
|
541
|
+
# @driver.find_element :tag, "button"
|
542
|
+
# end # The result of `find_element :tag, "button"`
|
543
|
+
#
|
544
|
+
def within_context(context)
|
545
|
+
existing_context = current_context
|
546
|
+
set_context context
|
547
|
+
if block_given?
|
548
|
+
result = yield
|
549
|
+
set_context existing_context
|
550
|
+
result
|
551
|
+
else
|
552
|
+
set_context existing_context
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
# Change to the default context. This is equivalent to `set_context nil`.
|
557
|
+
#
|
558
|
+
# @example
|
559
|
+
#
|
560
|
+
# @driver.switch_to_default_context
|
561
|
+
#
|
562
|
+
def switch_to_default_context
|
563
|
+
set_context nil
|
564
|
+
end
|
565
|
+
end # module Device
|
566
|
+
end # module Core
|
567
|
+
end # module Appium
|