appium_lib_core 4.1.0 → 9.2.1
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/CHANGELOG.md +352 -270
- data/README.md +68 -16
- data/Rakefile +8 -20
- data/Steepfile +11 -0
- data/appium_lib_core.gemspec +13 -15
- data/bin/console +0 -4
- data/lib/appium_lib_core/android/device/auth_finger_print.rb +4 -1
- data/lib/appium_lib_core/android/device/clipboard.rb +4 -2
- data/lib/appium_lib_core/android/device/emulator.rb +11 -5
- data/lib/appium_lib_core/android/device/network.rb +10 -0
- data/lib/appium_lib_core/android/device/performance.rb +3 -0
- data/lib/appium_lib_core/android/device/screen.rb +5 -1
- data/lib/appium_lib_core/android/device.rb +83 -20
- data/lib/appium_lib_core/common/base/bridge.rb +238 -95
- data/lib/appium_lib_core/common/base/capabilities.rb +21 -8
- data/lib/appium_lib_core/common/{command/mjsonwp.rb → base/device_ime.rb} +33 -12
- data/lib/appium_lib_core/common/base/driver.rb +263 -334
- data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
- data/lib/appium_lib_core/common/base/has_location.rb +80 -0
- data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
- data/lib/appium_lib_core/common/base/http_default.rb +22 -38
- data/lib/appium_lib_core/{ios/uiautomation/bridge.rb → common/base/remote_status.rb} +9 -8
- data/lib/appium_lib_core/common/base/rotable.rb +62 -0
- data/lib/appium_lib_core/common/base/screenshot.rb +10 -10
- data/lib/appium_lib_core/common/base/search_context.rb +98 -172
- data/lib/appium_lib_core/common/base.rb +1 -5
- data/lib/appium_lib_core/common/command.rb +244 -4
- data/lib/appium_lib_core/common/device/app_management.rb +2 -26
- data/lib/appium_lib_core/common/device/context.rb +1 -5
- data/lib/appium_lib_core/common/device/image_comparison.rb +27 -10
- data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
- data/lib/appium_lib_core/common/device/{touch_actions.rb → orientation.rb} +6 -10
- data/lib/appium_lib_core/common/device/screen_record.rb +8 -2
- data/lib/appium_lib_core/common/error.rb +5 -5
- data/lib/appium_lib_core/common/log.rb +5 -4
- data/lib/appium_lib_core/common/wait.rb +38 -6
- data/lib/appium_lib_core/device.rb +3 -9
- data/lib/appium_lib_core/driver.rb +207 -164
- data/lib/appium_lib_core/{patch.rb → element.rb} +64 -26
- data/lib/appium_lib_core/ios/device/clipboard.rb +4 -2
- data/lib/appium_lib_core/ios/xcuitest/device.rb +2 -0
- data/lib/appium_lib_core/{common/base/command.rb → mac2/bridge.rb} +9 -8
- data/lib/appium_lib_core/mac2/device/screen.rb +48 -0
- data/lib/appium_lib_core/mac2/device.rb +92 -0
- data/lib/appium_lib_core/{ios.rb → mac2.rb} +2 -5
- data/lib/appium_lib_core/support/event_firing_bridge.rb +57 -0
- data/lib/appium_lib_core/version.rb +2 -2
- data/lib/appium_lib_core.rb +23 -10
- data/rbs_collection.lock.yaml +252 -0
- data/rbs_collection.yaml +15 -0
- data/sig/gems/selenium/abstract_event_listener.rbs +8 -0
- data/sig/gems/selenium/capabilities.rbs +8 -0
- data/sig/gems/selenium/common.rbs +10 -0
- data/sig/gems/selenium/default.rbs +10 -0
- data/sig/gems/selenium/driver.rbs +7 -0
- data/sig/gems/selenium/has_session_id.rbs +8 -0
- data/sig/gems/selenium/has_web_storage.rbs +8 -0
- data/sig/gems/selenium/uploads_files.rbs +8 -0
- data/sig/lib/appium_lib_core/common/base/capabilities.rbs +9 -0
- data/sig/lib/appium_lib_core/common/base/driver.rbs +167 -0
- data/sig/lib/appium_lib_core/common/base/driver_settings.rbs +15 -0
- data/sig/lib/appium_lib_core/common/base/has_location.rbs +13 -0
- data/sig/lib/appium_lib_core/common/base/has_network_connection.rbs +19 -0
- data/sig/lib/appium_lib_core/common/base/http_default.rbs +38 -0
- data/sig/lib/appium_lib_core/common/base/platform.rbs +7 -0
- data/sig/lib/appium_lib_core/common/base/remote_status.rbs +9 -0
- data/sig/lib/appium_lib_core/common/base/rotable.rbs +17 -0
- data/sig/lib/appium_lib_core/common/base/screenshot.rbs +19 -0
- data/sig/lib/appium_lib_core/common/device/battery_status.rbs +13 -0
- data/sig/lib/appium_lib_core/common/wait.rbs +31 -0
- data/sig/lib/appium_lib_core/device.rbs +21 -0
- data/sig/lib/appium_lib_core/driver.rbs +200 -0
- data/sig/lib/appium_lib_core/ios/xcuitest/device/battery.rbs +15 -0
- data/sig/lib/appium_lib_core/version.rbs +7 -0
- data/sig/lib/appium_lib_core.rbs +8 -0
- metadata +88 -111
- data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
- data/.github/contributing.md +0 -26
- data/.github/issue_template.md +0 -20
- data/.github/workflows/unittest.yml +0 -68
- data/.gitignore +0 -18
- data/.rubocop.yml +0 -58
- data/azure-pipelines.yml +0 -15
- data/ci-jobs/functional/android_setup.yml +0 -3
- data/ci-jobs/functional/ios_setup.yml +0 -7
- data/ci-jobs/functional/publish_test_result.yml +0 -18
- data/ci-jobs/functional/run_appium.yml +0 -25
- data/ci-jobs/functional/start-emulator.sh +0 -26
- data/ci-jobs/functional_test.yml +0 -298
- data/docs/mobile_command.md +0 -34
- data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
- data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
- data/lib/appium_lib_core/common/command/common.rb +0 -110
- data/lib/appium_lib_core/common/command/w3c.rb +0 -56
- data/lib/appium_lib_core/common/device/value.rb +0 -52
- data/lib/appium_lib_core/common/touch_action/multi_touch.rb +0 -56
- data/lib/appium_lib_core/common/touch_action/touch_actions.rb +0 -203
- data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
- data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
- data/release_notes.md +0 -816
- data/script/commands.rb +0 -200
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
require 'uri'
|
|
16
16
|
|
|
17
17
|
module Appium
|
|
18
|
+
# The struct for 'location'
|
|
19
|
+
Location = Struct.new(:latitude, :longitude, :altitude)
|
|
20
|
+
|
|
18
21
|
module Core
|
|
19
22
|
module Android
|
|
20
23
|
autoload :Uiautomator1, 'appium_lib_core/android'
|
|
@@ -23,33 +26,30 @@ module Appium
|
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
module Ios
|
|
26
|
-
autoload :Uiautomation, 'appium_lib_core/ios'
|
|
27
29
|
autoload :Xcuitest, 'appium_lib_core/ios_xcuitest'
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
autoload :Mac2, 'appium_lib_core/mac2'
|
|
33
|
+
|
|
30
34
|
autoload :Windows, 'appium_lib_core/windows'
|
|
31
35
|
|
|
32
36
|
# This options affects only client side as <code>:appium_lib</code> key.<br>
|
|
33
37
|
# Read {::Appium::Core::Driver} about each attribute
|
|
34
38
|
class Options
|
|
35
|
-
attr_reader :custom_url, :default_wait,
|
|
39
|
+
attr_reader :custom_url, :default_wait,
|
|
36
40
|
:port, :wait_timeout, :wait_interval, :listener,
|
|
37
41
|
:direct_connect, :enable_idempotency_header
|
|
38
42
|
|
|
39
43
|
def initialize(appium_lib_opts)
|
|
40
44
|
@custom_url = appium_lib_opts.fetch :server_url, nil
|
|
41
|
-
@default_wait = appium_lib_opts.fetch :wait,
|
|
45
|
+
@default_wait = appium_lib_opts.fetch :wait, nil
|
|
42
46
|
@enable_idempotency_header = appium_lib_opts.fetch :enable_idempotency_header, true
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
@export_session = appium_lib_opts.fetch :export_session, false
|
|
46
|
-
@export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session
|
|
47
|
-
|
|
48
|
-
@direct_connect = appium_lib_opts.fetch :direct_connect, false
|
|
48
|
+
@direct_connect = appium_lib_opts.fetch :direct_connect, true
|
|
49
49
|
|
|
50
50
|
@port = appium_lib_opts.fetch :port, Driver::DEFAULT_APPIUM_PORT
|
|
51
51
|
|
|
52
|
-
# timeout and interval used in ::Appium::
|
|
52
|
+
# timeout and interval used in ::Appium::Commn.wait/wait_true
|
|
53
53
|
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
|
|
54
54
|
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
|
|
55
55
|
|
|
@@ -74,6 +74,13 @@ module Appium
|
|
|
74
74
|
path: 'directConnectPath'
|
|
75
75
|
}.freeze
|
|
76
76
|
|
|
77
|
+
W3C_KEYS = {
|
|
78
|
+
protocol: 'appium:directConnectProtocol',
|
|
79
|
+
host: 'appium:directConnectHost',
|
|
80
|
+
port: 'appium:directConnectPort',
|
|
81
|
+
path: 'appium:directConnectPath'
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
77
84
|
# @return [string] Returns a protocol such as http/https
|
|
78
85
|
attr_reader :protocol
|
|
79
86
|
|
|
@@ -87,17 +94,17 @@ module Appium
|
|
|
87
94
|
attr_reader :path
|
|
88
95
|
|
|
89
96
|
def initialize(capabilities)
|
|
90
|
-
@protocol = capabilities[KEYS[:protocol]]
|
|
91
|
-
@host = capabilities[KEYS[:host]]
|
|
92
|
-
@port = capabilities[KEYS[:port]]
|
|
93
|
-
@path = capabilities[KEYS[:path]]
|
|
97
|
+
@protocol = capabilities[W3C_KEYS[:protocol]] || capabilities[KEYS[:protocol]]
|
|
98
|
+
@host = capabilities[W3C_KEYS[:host]] || capabilities[KEYS[:host]]
|
|
99
|
+
@port = capabilities[W3C_KEYS[:port]] || capabilities[KEYS[:port]]
|
|
100
|
+
@path = capabilities[W3C_KEYS[:path]] || capabilities[KEYS[:path]]
|
|
94
101
|
end
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
class Driver
|
|
98
105
|
include Waitable
|
|
99
106
|
|
|
100
|
-
# Selenium webdriver capabilities
|
|
107
|
+
# Selenium webdriver capabilities, but the value is provided capabilities basis.
|
|
101
108
|
# @return [Core::Base::Capabilities]
|
|
102
109
|
attr_reader :caps
|
|
103
110
|
|
|
@@ -118,7 +125,7 @@ module Appium
|
|
|
118
125
|
|
|
119
126
|
# Automation name sent to appium server or received by server.<br>
|
|
120
127
|
# If automation_name is <code>nil</code>, it is not set both client side and server side.
|
|
121
|
-
# @return [
|
|
128
|
+
# @return [Symbol]
|
|
122
129
|
attr_reader :automation_name
|
|
123
130
|
|
|
124
131
|
# Custom URL for the selenium server. If set this attribute, ruby_lib_core try to handshake to the custom url.<br>
|
|
@@ -126,18 +133,10 @@ module Appium
|
|
|
126
133
|
# @return [String]
|
|
127
134
|
attr_reader :custom_url
|
|
128
135
|
|
|
129
|
-
# Export session id to textfile in /tmp for 3rd party tools. False by default.
|
|
130
|
-
# @return [Boolean]
|
|
131
|
-
attr_reader :export_session
|
|
132
|
-
# @return [String] By default, session id is exported in '/tmp/appium_lib_session'
|
|
133
|
-
attr_reader :export_session_path
|
|
134
|
-
|
|
135
136
|
# Default wait time for elements to appear in Appium server side.
|
|
136
|
-
# Defaults to {::Appium::Core::Driver::DEFAULT_IMPLICIT_WAIT}.<br>
|
|
137
137
|
# Provide <code>{ appium_lib: { wait: 30 } }</code> to {::Appium::Core.for}
|
|
138
138
|
# @return [Integer]
|
|
139
139
|
attr_reader :default_wait
|
|
140
|
-
DEFAULT_IMPLICIT_WAIT = 0
|
|
141
140
|
|
|
142
141
|
# Appium's server port. 4723 is by default. Defaults to {::Appium::Core::Driver::DEFAULT_APPIUM_PORT}.<br>
|
|
143
142
|
# Provide <code>{ appium_lib: { port: 8080 } }</code> to {::Appium::Core.for}.
|
|
@@ -175,7 +174,8 @@ module Appium
|
|
|
175
174
|
# - <code>directConnectPort</code>
|
|
176
175
|
# - <code>directConnectPath</code>
|
|
177
176
|
#
|
|
178
|
-
#
|
|
177
|
+
# ignore them if this parameter is <code>false</code>. Defaults to true.
|
|
178
|
+
# These keys can have <code>appium:</code> prefix.
|
|
179
179
|
#
|
|
180
180
|
# @return [Bool]
|
|
181
181
|
attr_reader :direct_connect
|
|
@@ -185,8 +185,6 @@ module Appium
|
|
|
185
185
|
# @option opts [Hash] :caps Appium capabilities.
|
|
186
186
|
# @option opts [Hash] :capabilities The same as :caps.
|
|
187
187
|
# This param is for compatibility with Selenium WebDriver format
|
|
188
|
-
# @option opts [Hash] :desired_capabilities The same as :caps.
|
|
189
|
-
# This param is for compatibility with Selenium WebDriver format
|
|
190
188
|
# @option opts [Appium::Core::Options] :appium_lib Capabilities affect only ruby client
|
|
191
189
|
# @option opts [String] :url The same as :custom_url in :appium_lib.
|
|
192
190
|
# This param is for compatibility with Selenium WebDriver format
|
|
@@ -197,10 +195,8 @@ module Appium
|
|
|
197
195
|
#
|
|
198
196
|
# # format 1
|
|
199
197
|
# @core = Appium::Core.for caps: {...}, appium_lib: {...}
|
|
200
|
-
# # format 2. 'capabilities:'
|
|
198
|
+
# # format 2. 'capabilities:' is also available instead of 'caps:'.
|
|
201
199
|
# @core = Appium::Core.for url: "http://127.0.0.1:8080/wd/hub", capabilities: {...}, appium_lib: {...}
|
|
202
|
-
# # format 3. 'appium_lib: {...}' can be blank
|
|
203
|
-
# @core = Appium::Core.for url: "http://127.0.0.1:8080/wd/hub", desired_capabilities: {...}
|
|
204
200
|
#
|
|
205
201
|
#
|
|
206
202
|
# require 'rubygems'
|
|
@@ -216,7 +212,6 @@ module Appium
|
|
|
216
212
|
# app: '/path/to/MyiOS.app'
|
|
217
213
|
# },
|
|
218
214
|
# appium_lib: {
|
|
219
|
-
# export_session: false,
|
|
220
215
|
# port: 8080,
|
|
221
216
|
# wait: 0,
|
|
222
217
|
# wait_timeout: 20,
|
|
@@ -228,7 +223,7 @@ module Appium
|
|
|
228
223
|
# @core.start_driver # Connect to 'http://127.0.0.1:8080/wd/hub' because of 'port: 8080'
|
|
229
224
|
#
|
|
230
225
|
# # Start iOS driver with .zip file over HTTP
|
|
231
|
-
# #
|
|
226
|
+
# # 'capabilities:' is also available instead of 'caps:'. Either is fine.
|
|
232
227
|
# opts = {
|
|
233
228
|
# capabilities: {
|
|
234
229
|
# platformName: :ios,
|
|
@@ -239,7 +234,6 @@ module Appium
|
|
|
239
234
|
# },
|
|
240
235
|
# appium_lib: {
|
|
241
236
|
# server_url: 'http://custom-host:8080/wd/hub.com',
|
|
242
|
-
# export_session: false,
|
|
243
237
|
# wait: 0,
|
|
244
238
|
# wait_timeout: 20,
|
|
245
239
|
# wait_interval: 0.3,
|
|
@@ -252,7 +246,7 @@ module Appium
|
|
|
252
246
|
# # Start iOS driver as another format. 'url' is available like below
|
|
253
247
|
# opts = {
|
|
254
248
|
# url: "http://custom-host:8080/wd/hub.com",
|
|
255
|
-
#
|
|
249
|
+
# capabilities: {
|
|
256
250
|
# platformName: :ios,
|
|
257
251
|
# platformVersion: '11.0',
|
|
258
252
|
# deviceName: 'iPhone Simulator',
|
|
@@ -260,7 +254,6 @@ module Appium
|
|
|
260
254
|
# app: '/path/to/MyiOS.app'
|
|
261
255
|
# },
|
|
262
256
|
# appium_lib: {
|
|
263
|
-
# export_session: false,
|
|
264
257
|
# wait: 0,
|
|
265
258
|
# wait_timeout: 20,
|
|
266
259
|
# wait_interval: 0.3,
|
|
@@ -270,8 +263,58 @@ module Appium
|
|
|
270
263
|
# @core = Appium::Core.for(opts) # create a core driver with 'opts' and extend methods into 'self'
|
|
271
264
|
# @core.start_driver # start driver with 'url'. Connect to 'http://custom-host:8080/wd/hub.com'
|
|
272
265
|
#
|
|
266
|
+
# # With a custom listener
|
|
267
|
+
# class CustomListener < ::Selenium::WebDriver::Support::AbstractEventListener
|
|
268
|
+
# // something
|
|
269
|
+
# end
|
|
270
|
+
# capabilities: {
|
|
271
|
+
# platformName: :ios,
|
|
272
|
+
# platformVersion: '11.0',
|
|
273
|
+
# deviceName: 'iPhone Simulator',
|
|
274
|
+
# automationName: 'XCUITest',
|
|
275
|
+
# app: '/path/to/MyiOS.app'
|
|
276
|
+
# },
|
|
277
|
+
# appium_lib: {
|
|
278
|
+
# listener: CustomListener.new,
|
|
279
|
+
# }
|
|
280
|
+
# @core = Appium::Core.for capabilities: capabilities, appium_lib: appium_lib
|
|
281
|
+
# @core.start_driver
|
|
282
|
+
#
|
|
273
283
|
def self.for(opts = {})
|
|
274
|
-
new(opts)
|
|
284
|
+
new.setup_for_new_session(opts)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Attach to an existing session. The main usage of this method is to attach to
|
|
288
|
+
# an existing session for debugging. The generated driver instance has the capabilities which
|
|
289
|
+
# has the given automationName and platformName only since the W3C WebDriver spec does not provide
|
|
290
|
+
# an endpoint to get running session's capabilities.
|
|
291
|
+
#
|
|
292
|
+
#
|
|
293
|
+
# @param [String] The session id to attach to.
|
|
294
|
+
# @param [String] url The WebDriver URL to attach to with the session_id.
|
|
295
|
+
# @param [String] automation_name The platform name to keep in the dummy capabilities
|
|
296
|
+
# @param [String] platform_name The automation name to keep in the dummy capabilities
|
|
297
|
+
# @return [Selenium::WebDriver] A new driver instance with the given session id.
|
|
298
|
+
#
|
|
299
|
+
# @example
|
|
300
|
+
#
|
|
301
|
+
# new_driver = ::Appium::Core::Driver.attach_to(
|
|
302
|
+
# driver.session_id, # The 'driver' has an existing session id
|
|
303
|
+
# url: 'http://127.0.0.1:4723/wd/hub', automation_name: 'UiAutomator2', platform_name: 'Android'
|
|
304
|
+
# )
|
|
305
|
+
# new_driver.page_source # for example
|
|
306
|
+
#
|
|
307
|
+
def self.attach_to(
|
|
308
|
+
session_id, url: nil, automation_name: nil, platform_name: nil,
|
|
309
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }
|
|
310
|
+
)
|
|
311
|
+
new.attach_to(
|
|
312
|
+
session_id,
|
|
313
|
+
automation_name: automation_name,
|
|
314
|
+
platform_name: platform_name,
|
|
315
|
+
url: url,
|
|
316
|
+
http_client_ops: http_client_ops
|
|
317
|
+
)
|
|
275
318
|
end
|
|
276
319
|
|
|
277
320
|
private
|
|
@@ -282,17 +325,19 @@ module Appium
|
|
|
282
325
|
@delegate_target
|
|
283
326
|
end
|
|
284
327
|
|
|
285
|
-
public
|
|
286
|
-
|
|
287
328
|
# @private
|
|
288
|
-
def initialize
|
|
329
|
+
def initialize
|
|
289
330
|
@delegate_target = self # for testing purpose
|
|
290
331
|
@automation_name = nil # initialise before 'set_automation_name'
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
public
|
|
291
335
|
|
|
292
|
-
|
|
293
|
-
|
|
336
|
+
# @private
|
|
337
|
+
# Set up for a new session
|
|
338
|
+
def setup_for_new_session(opts = {})
|
|
339
|
+
@custom_url = opts.delete :url # to set the custom url as :url
|
|
294
340
|
|
|
295
|
-
@custom_url = opts.delete :url
|
|
296
341
|
@caps = get_caps(opts)
|
|
297
342
|
|
|
298
343
|
set_appium_lib_specific_values(get_appium_lib_opts(opts))
|
|
@@ -301,8 +346,7 @@ module Appium
|
|
|
301
346
|
set_automation_name
|
|
302
347
|
|
|
303
348
|
extend_for(device: @device, automation_name: @automation_name)
|
|
304
|
-
|
|
305
|
-
self # rubocop:disable Lint/Void
|
|
349
|
+
self
|
|
306
350
|
end
|
|
307
351
|
|
|
308
352
|
# Creates a new global driver and quits the old one if it exists.
|
|
@@ -313,7 +357,7 @@ module Appium
|
|
|
313
357
|
# @option http_client_ops [Hash] :http_client Custom HTTP Client
|
|
314
358
|
# @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
|
|
315
359
|
# @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
|
|
316
|
-
# @return [Selenium::WebDriver]
|
|
360
|
+
# @return [Selenium::WebDriver] A new driver instance
|
|
317
361
|
#
|
|
318
362
|
# @example
|
|
319
363
|
#
|
|
@@ -324,7 +368,7 @@ module Appium
|
|
|
324
368
|
#
|
|
325
369
|
# # Start iOS driver
|
|
326
370
|
# opts = {
|
|
327
|
-
#
|
|
371
|
+
# capabilities: {
|
|
328
372
|
# platformName: :ios,
|
|
329
373
|
# platformVersion: '11.0',
|
|
330
374
|
# deviceName: 'iPhone Simulator',
|
|
@@ -350,7 +394,9 @@ module Appium
|
|
|
350
394
|
|
|
351
395
|
def start_driver(server_url: nil,
|
|
352
396
|
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
|
353
|
-
|
|
397
|
+
|
|
398
|
+
@custom_url ||= "http://127.0.0.1:#{@port}/wd/hub"
|
|
399
|
+
@custom_url = server_url unless server_url.nil?
|
|
354
400
|
|
|
355
401
|
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
|
356
402
|
open_timeout: http_client_ops.delete(:open_timeout),
|
|
@@ -358,35 +404,35 @@ module Appium
|
|
|
358
404
|
|
|
359
405
|
if @enable_idempotency_header
|
|
360
406
|
if @http_client.instance_variable_defined? :@additional_headers
|
|
361
|
-
@http_client.
|
|
407
|
+
@http_client.set_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency], SecureRandom.uuid
|
|
362
408
|
else
|
|
363
409
|
::Appium::Logger.warn 'No additional_headers attribute in this http client instance'
|
|
364
410
|
end
|
|
365
411
|
end
|
|
366
412
|
|
|
367
413
|
begin
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
414
|
+
@driver = ::Appium::Core::Base::Driver.new(listener: @listener,
|
|
415
|
+
http_client: @http_client,
|
|
416
|
+
capabilities: @caps, # ::Appium::Core::Base::Capabilities
|
|
371
417
|
url: @custom_url,
|
|
372
|
-
|
|
418
|
+
wait_timeout: @wait_timeout,
|
|
419
|
+
wait_interval: @wait_interval)
|
|
373
420
|
|
|
374
421
|
if @direct_connect
|
|
375
422
|
d_c = DirectConnections.new(@driver.capabilities)
|
|
376
423
|
@driver.update_sending_request_to(protocol: d_c.protocol, host: d_c.host, port: d_c.port, path: d_c.path)
|
|
377
424
|
end
|
|
378
|
-
|
|
379
|
-
# export session
|
|
380
|
-
write_session_id(@driver.session_id, @export_session_path) if @export_session
|
|
381
425
|
rescue Errno::ECONNREFUSED
|
|
382
426
|
raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
|
383
427
|
end
|
|
384
428
|
|
|
385
429
|
if @http_client.instance_variable_defined? :@additional_headers
|
|
386
430
|
# We only need the key for a new session request. Should remove it for other following commands.
|
|
387
|
-
@http_client.
|
|
431
|
+
@http_client.delete_additional_header Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
|
|
388
432
|
end
|
|
389
433
|
|
|
434
|
+
# TODO: this method can be removed after releasing Appium 2.0, and after a while
|
|
435
|
+
# since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
|
|
390
436
|
# If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
|
|
391
437
|
# Since @automation_name is set only client side before start_driver is called.
|
|
392
438
|
set_automation_name_if_nil
|
|
@@ -396,20 +442,54 @@ module Appium
|
|
|
396
442
|
@driver
|
|
397
443
|
end
|
|
398
444
|
|
|
399
|
-
private
|
|
445
|
+
# @private
|
|
446
|
+
# Attach to an existing session
|
|
447
|
+
def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
|
|
448
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
|
400
449
|
|
|
401
|
-
|
|
402
|
-
|
|
450
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
|
|
451
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
|
|
452
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
|
|
453
|
+
|
|
454
|
+
@custom_url = url
|
|
455
|
+
|
|
456
|
+
# use lowercase internally
|
|
457
|
+
@automation_name = convert_downcase(automation_name)
|
|
458
|
+
@device = convert_downcase(platform_name)
|
|
459
|
+
|
|
460
|
+
extend_for(device: @device, automation_name: @automation_name)
|
|
461
|
+
|
|
462
|
+
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
|
463
|
+
open_timeout: http_client_ops.delete(:open_timeout),
|
|
464
|
+
read_timeout: http_client_ops.delete(:read_timeout)
|
|
465
|
+
|
|
466
|
+
# Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
|
|
467
|
+
# the new session request, this it does not needed.
|
|
403
468
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
469
|
+
begin
|
|
470
|
+
# included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
|
|
471
|
+
@driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
|
|
472
|
+
url: @custom_url,
|
|
473
|
+
listener: @listener,
|
|
474
|
+
existing_session_id: session_id,
|
|
475
|
+
automation_name: automation_name,
|
|
476
|
+
platform_name: platform_name)
|
|
477
|
+
rescue Errno::ECONNREFUSED
|
|
478
|
+
raise ::Appium::Core::Error::SessionNotCreatedError,
|
|
479
|
+
"ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
|
480
|
+
end
|
|
407
481
|
|
|
408
|
-
|
|
482
|
+
@driver
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
|
|
486
|
+
http_client || Appium::Core::Base::Http::Default.new(open_timeout: open_timeout, read_timeout: read_timeout)
|
|
409
487
|
end
|
|
410
488
|
|
|
411
489
|
# Ignore setting default wait if the target driver has no implementation
|
|
412
490
|
def set_implicit_wait_by_default(wait)
|
|
491
|
+
return if @default_wait.nil?
|
|
492
|
+
|
|
413
493
|
@driver.manage.timeouts.implicit_wait = wait
|
|
414
494
|
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
|
415
495
|
unless e.message.include?('The operation requested is not yet implemented')
|
|
@@ -420,9 +500,7 @@ module Appium
|
|
|
420
500
|
{}
|
|
421
501
|
end
|
|
422
502
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
# Quits the driver
|
|
503
|
+
# [Deprecated] Quits the driver. This method is the same as @driver.quit
|
|
426
504
|
# @return [void]
|
|
427
505
|
#
|
|
428
506
|
# @example
|
|
@@ -430,12 +508,14 @@ module Appium
|
|
|
430
508
|
# @core.quit_driver
|
|
431
509
|
#
|
|
432
510
|
def quit_driver
|
|
511
|
+
::Appium::Logger.warn('[DEPRECATION] quit_driver will be removed. Please use @driver.quit instead.')
|
|
433
512
|
@driver.quit
|
|
434
513
|
rescue # rubocop:disable Style/RescueStandardError
|
|
435
514
|
nil
|
|
436
515
|
end
|
|
437
516
|
|
|
438
|
-
# Returns the server's version info
|
|
517
|
+
# Returns the server's version info. This method calls +driver.remote_status+ internally
|
|
518
|
+
#
|
|
439
519
|
# @return [Hash]
|
|
440
520
|
#
|
|
441
521
|
# @example
|
|
@@ -449,58 +529,43 @@ module Appium
|
|
|
449
529
|
# }
|
|
450
530
|
# }
|
|
451
531
|
#
|
|
452
|
-
# Returns blank hash
|
|
532
|
+
# Returns blank hash in a case +driver.remote_status+ got an error
|
|
533
|
+
# such as Selenium Grid. It returns 500 error against 'remote_status'.
|
|
453
534
|
#
|
|
454
535
|
# @example
|
|
455
536
|
#
|
|
456
537
|
# @core.appium_server_version #=> {}
|
|
457
538
|
#
|
|
458
539
|
def appium_server_version
|
|
459
|
-
@driver.
|
|
460
|
-
rescue Selenium::WebDriver::Error::ServerError => e
|
|
461
|
-
raise ::Appium::Core::Error::ServerError unless e.message.include?('status code 500')
|
|
540
|
+
return {} if @driver.nil?
|
|
462
541
|
|
|
463
|
-
|
|
542
|
+
@driver.remote_status
|
|
543
|
+
rescue StandardError
|
|
544
|
+
# Ignore error case in a case the target appium server
|
|
545
|
+
# does not support `/status` API.
|
|
464
546
|
{}
|
|
465
547
|
end
|
|
466
548
|
|
|
467
|
-
|
|
468
|
-
# @return [Array<Integer>]
|
|
469
|
-
#
|
|
470
|
-
# @example
|
|
471
|
-
#
|
|
472
|
-
# @core.platform_version #=> [10,1,1]
|
|
473
|
-
#
|
|
474
|
-
def platform_version
|
|
475
|
-
p_version = @driver.capabilities['platformVersion'] || @driver.session_capabilities['platformVersion']
|
|
476
|
-
p_version.split('.').map(&:to_i)
|
|
477
|
-
end
|
|
549
|
+
private
|
|
478
550
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
#
|
|
486
|
-
# @core.screenshot '/tmp/hi.png' #=> nil
|
|
487
|
-
# # same as '@driver.save_screenshot png_save_path'
|
|
488
|
-
#
|
|
489
|
-
def screenshot(png_save_path)
|
|
490
|
-
::Appium::Logger.warn '[DEPRECATION] screenshot will be removed. Please use driver.save_screenshot instead.'
|
|
491
|
-
@driver.save_screenshot png_save_path
|
|
551
|
+
def convert_to_symbol(value)
|
|
552
|
+
if value.nil?
|
|
553
|
+
value
|
|
554
|
+
else
|
|
555
|
+
value.to_sym
|
|
556
|
+
end
|
|
492
557
|
end
|
|
493
558
|
|
|
494
|
-
private
|
|
495
|
-
|
|
496
559
|
# @private
|
|
497
560
|
def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
|
|
498
561
|
extend Appium::Core
|
|
499
562
|
extend Appium::Core::Device
|
|
500
563
|
|
|
501
|
-
|
|
564
|
+
sym_automation_name = convert_to_symbol(automation_name)
|
|
565
|
+
|
|
566
|
+
case convert_to_symbol(device)
|
|
502
567
|
when :android
|
|
503
|
-
case
|
|
568
|
+
case sym_automation_name
|
|
504
569
|
when :espresso
|
|
505
570
|
::Appium::Core::Android::Espresso::Bridge.for self
|
|
506
571
|
when :uiautomator2
|
|
@@ -511,28 +576,26 @@ module Appium
|
|
|
511
576
|
::Appium::Core::Android::Uiautomator1::Bridge.for self
|
|
512
577
|
end
|
|
513
578
|
when :ios, :tvos
|
|
514
|
-
case
|
|
579
|
+
case sym_automation_name
|
|
515
580
|
when :safari
|
|
516
581
|
::Appium::Logger.debug('SafariDriver for iOS')
|
|
517
|
-
|
|
582
|
+
else # XCUITest
|
|
518
583
|
::Appium::Core::Ios::Xcuitest::Bridge.for self
|
|
519
|
-
else # default and UIAutomation
|
|
520
|
-
::Appium::Core::Ios::Uiautomation::Bridge.for self
|
|
521
584
|
end
|
|
522
585
|
when :mac
|
|
523
|
-
case
|
|
586
|
+
case sym_automation_name
|
|
524
587
|
when :safari
|
|
525
588
|
::Appium::Logger.debug('SafariDriver for macOS')
|
|
526
589
|
when :gecko
|
|
527
590
|
::Appium::Logger.debug('Gecko Driver for macOS')
|
|
528
591
|
when :mac2
|
|
529
|
-
::Appium::
|
|
592
|
+
::Appium::Core::Mac2::Bridge.for self
|
|
530
593
|
else
|
|
531
594
|
# no Mac specific extentions
|
|
532
595
|
::Appium::Logger.debug('macOS Native')
|
|
533
596
|
end
|
|
534
597
|
when :windows
|
|
535
|
-
case
|
|
598
|
+
case sym_automation_name
|
|
536
599
|
when :gecko
|
|
537
600
|
::Appium::Logger.debug('Gecko Driver for Windows')
|
|
538
601
|
else
|
|
@@ -542,7 +605,7 @@ module Appium
|
|
|
542
605
|
# https://github.com/Samsung/appium-tizen-driver
|
|
543
606
|
::Appium::Logger.debug('tizen')
|
|
544
607
|
else
|
|
545
|
-
case
|
|
608
|
+
case sym_automation_name
|
|
546
609
|
when :youiengine
|
|
547
610
|
# https://github.com/YOU-i-Labs/appium-youiengine-driver
|
|
548
611
|
::Appium::Logger.debug('YouiEngine')
|
|
@@ -559,36 +622,9 @@ module Appium
|
|
|
559
622
|
self
|
|
560
623
|
end
|
|
561
624
|
|
|
562
|
-
# @private
|
|
563
|
-
def validate_keys(opts)
|
|
564
|
-
flatten_ops = flatten_hash_keys(opts)
|
|
565
|
-
|
|
566
|
-
# FIXME: Remove 'desired_capabilities' in the next major Selenium update
|
|
567
|
-
unless opts.member?(:caps) || opts.member?(:capabilities) || opts.member?(:desired_capabilities)
|
|
568
|
-
raise Error::NoCapabilityError
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
if !opts.member?(:appium_lib) && flatten_ops.member?(:appium_lib)
|
|
572
|
-
raise Error::CapabilityStructureError, 'Please check the value of appium_lib in the capability'
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
true
|
|
576
|
-
end
|
|
577
|
-
|
|
578
|
-
# @private
|
|
579
|
-
def flatten_hash_keys(hash, flatten_keys_result = [])
|
|
580
|
-
hash.each do |key, value|
|
|
581
|
-
flatten_keys_result << key
|
|
582
|
-
flatten_hash_keys(value, flatten_keys_result) if value.is_a?(Hash)
|
|
583
|
-
end
|
|
584
|
-
|
|
585
|
-
flatten_keys_result
|
|
586
|
-
end
|
|
587
|
-
|
|
588
625
|
# @private
|
|
589
626
|
def get_caps(opts)
|
|
590
|
-
|
|
591
|
-
Core::Base::Capabilities.create_capabilities(opts[:caps] || opts[:capabilities] || opts[:desired_capabilities] || {})
|
|
627
|
+
Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
|
|
592
628
|
end
|
|
593
629
|
|
|
594
630
|
# @private
|
|
@@ -596,21 +632,27 @@ module Appium
|
|
|
596
632
|
opts[:appium_lib] || {}
|
|
597
633
|
end
|
|
598
634
|
|
|
635
|
+
# @private
|
|
636
|
+
def get_app
|
|
637
|
+
get_cap 'app'
|
|
638
|
+
end
|
|
639
|
+
|
|
599
640
|
# @private
|
|
600
641
|
# Path to the .apk, .app or .app.zip.
|
|
601
642
|
# The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
|
|
602
643
|
# Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
|
|
603
644
|
def set_app_path
|
|
604
|
-
|
|
605
|
-
return
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
645
|
+
# FIXME: maybe `:app` should check `app` as well.
|
|
646
|
+
return unless @caps && get_app && !get_app.empty?
|
|
647
|
+
return if get_app =~ URI::DEFAULT_PARSER.make_regexp
|
|
648
|
+
|
|
649
|
+
app_path = File.expand_path(get_app)
|
|
650
|
+
@caps['app'] = if File.exist? app_path
|
|
651
|
+
app_path
|
|
652
|
+
else
|
|
653
|
+
::Appium::Logger.warn("Use #{get_app} directly since #{app_path} does not exist.")
|
|
654
|
+
get_app
|
|
655
|
+
end
|
|
614
656
|
end
|
|
615
657
|
|
|
616
658
|
# @private
|
|
@@ -623,9 +665,6 @@ module Appium
|
|
|
623
665
|
|
|
624
666
|
@default_wait = opts.default_wait
|
|
625
667
|
|
|
626
|
-
@export_session = opts.export_session
|
|
627
|
-
@export_session_path = opts.export_session_path
|
|
628
|
-
|
|
629
668
|
@port = opts.port
|
|
630
669
|
|
|
631
670
|
@wait_timeout = opts.wait_timeout
|
|
@@ -639,36 +678,40 @@ module Appium
|
|
|
639
678
|
# @private
|
|
640
679
|
def set_appium_device
|
|
641
680
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
|
642
|
-
@device =
|
|
681
|
+
@device = get_cap 'platformName'
|
|
643
682
|
return @device unless @device
|
|
644
683
|
|
|
645
|
-
@device = @device
|
|
684
|
+
@device = convert_to_symbol(convert_downcase(@device))
|
|
646
685
|
end
|
|
647
686
|
|
|
648
687
|
# @private
|
|
649
688
|
def set_automation_name
|
|
650
|
-
|
|
651
|
-
@automation_name = if
|
|
652
|
-
|
|
653
|
-
|
|
689
|
+
candidate = get_cap 'automationName'
|
|
690
|
+
@automation_name = candidate if candidate
|
|
691
|
+
@automation_name = convert_to_symbol(convert_downcase(@automation_name)) if @automation_name
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
# @private
|
|
695
|
+
def convert_downcase(value)
|
|
696
|
+
value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
|
|
654
697
|
end
|
|
655
698
|
|
|
656
699
|
# @private
|
|
657
700
|
def set_automation_name_if_nil
|
|
658
701
|
return unless @automation_name.nil?
|
|
659
702
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
703
|
+
automation_name = if @driver.capabilities['automationName']
|
|
704
|
+
@driver.capabilities['automationName'].downcase.strip.intern
|
|
705
|
+
end
|
|
706
|
+
@automation_name = convert_to_symbol automation_name
|
|
663
707
|
end
|
|
664
708
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
nil
|
|
709
|
+
def get_cap(name)
|
|
710
|
+
name_with_prefix = "appium:#{name}"
|
|
711
|
+
@caps[convert_to_symbol name] ||
|
|
712
|
+
@caps[name] ||
|
|
713
|
+
@caps[convert_to_symbol name_with_prefix] ||
|
|
714
|
+
@caps[name_with_prefix]
|
|
672
715
|
end
|
|
673
716
|
end # class Driver
|
|
674
717
|
end # module Core
|