appium_lib_core 4.1.0 → 9.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|