appium_lib_core 4.1.0 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +187 -273
- data/README.md +32 -12
- data/Rakefile +4 -0
- data/appium_lib_core.gemspec +5 -8
- data/bin/console +0 -4
- data/lib/appium_lib_core/android/device/auth_finger_print.rb +2 -1
- data/lib/appium_lib_core/android/device.rb +4 -4
- data/lib/appium_lib_core/common/base/bridge.rb +315 -90
- data/lib/appium_lib_core/common/base/capabilities.rb +8 -9
- data/lib/appium_lib_core/common/base/device_ime.rb +49 -0
- data/lib/appium_lib_core/common/base/driver.rb +222 -187
- 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 +1 -3
- data/lib/appium_lib_core/common/base/remote_status.rb +31 -0
- data/lib/appium_lib_core/common/base/rotable.rb +54 -0
- data/lib/appium_lib_core/common/base/screenshot.rb +6 -6
- data/lib/appium_lib_core/common/base/search_context.rb +20 -5
- data/lib/appium_lib_core/common/base.rb +1 -3
- data/lib/appium_lib_core/common/command.rb +255 -4
- data/lib/appium_lib_core/common/device/app_management.rb +8 -14
- data/lib/appium_lib_core/common/device/image_comparison.rb +12 -4
- data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
- data/lib/appium_lib_core/common/{command/mjsonwp.rb → device/orientation.rb} +14 -11
- data/lib/appium_lib_core/common/device/touch_actions.rb +2 -0
- data/lib/appium_lib_core/common/device/value.rb +6 -6
- data/lib/appium_lib_core/common/error.rb +4 -5
- data/lib/appium_lib_core/common/log.rb +4 -1
- data/lib/appium_lib_core/common/touch_action/multi_touch.rb +19 -0
- data/lib/appium_lib_core/common/touch_action/touch_actions.rb +16 -2
- data/lib/appium_lib_core/common/wait.rb +38 -6
- data/lib/appium_lib_core/device.rb +1 -5
- data/lib/appium_lib_core/driver.rb +166 -97
- data/lib/appium_lib_core/{patch.rb → element.rb} +66 -9
- data/lib/appium_lib_core/ios/uiautomation/patch.rb +1 -1
- 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/mac2.rb +17 -0
- data/lib/appium_lib_core/version.rb +2 -2
- data/lib/appium_lib_core/windows/device/app_management.rb +38 -0
- data/lib/appium_lib_core/windows/device.rb +2 -0
- data/lib/appium_lib_core.rb +20 -10
- metadata +30 -82
- 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/release_notes.md +0 -816
- data/script/commands.rb +0 -200
@@ -38,6 +38,8 @@ module Appium
|
|
38
38
|
#
|
39
39
|
# result = Appium::Core::Wait.until { @driver.find_element(:id, 'something') }
|
40
40
|
#
|
41
|
+
# result = Appium::Core::Wait.until(timeout: 30, message: 'timeout') { @driver.find_element(:id, 'something') }
|
42
|
+
#
|
41
43
|
# result = Appium::Core::Wait.until(object: 'some object') { |object|
|
42
44
|
# @driver.find_element(:id, object)
|
43
45
|
# }
|
@@ -82,6 +84,8 @@ module Appium
|
|
82
84
|
#
|
83
85
|
# Appium::Core::Wait.until_true { @driver.find_element(:id, 'something') }
|
84
86
|
#
|
87
|
+
# Appium::Core::Wait.until_true(timeout: 30) { @driver.find_element(:id, 'something') }
|
88
|
+
#
|
85
89
|
# Appium::Core::Wait.until_true(object: 'some object') { |object|
|
86
90
|
# @driver.find_element(:id, object)
|
87
91
|
# }
|
@@ -133,17 +137,31 @@ module Appium
|
|
133
137
|
# @param [String] message Exception message if timed out.
|
134
138
|
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
|
135
139
|
#
|
136
|
-
# @example
|
140
|
+
# @example With core instance
|
137
141
|
#
|
138
142
|
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
|
143
|
+
# @core.wait_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
|
144
|
+
#
|
145
|
+
# @core.wait_until_true { @driver.find_element :accessibility_id, 'something' }
|
146
|
+
# @core.wait_until_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
|
147
|
+
#
|
148
|
+
# @example With driver instance
|
149
|
+
#
|
150
|
+
# @driver.wait_true { |d| d.find_element :accessibility_id, 'something' }
|
151
|
+
# @driver.wait_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
|
139
152
|
#
|
140
|
-
|
153
|
+
# @driver.wait_until_true { |d| d.find_element :accessibility_id, 'something' }
|
154
|
+
# @driver.wait_until_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
|
155
|
+
#
|
156
|
+
def wait_until_true(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
|
141
157
|
Wait.until_true(timeout: timeout || @wait_timeout,
|
142
158
|
interval: interval || @wait_interval,
|
143
159
|
message: message,
|
144
160
|
ignored: ignored,
|
145
|
-
object: self
|
161
|
+
object: self,
|
162
|
+
&block)
|
146
163
|
end
|
164
|
+
alias wait_true wait_until_true
|
147
165
|
|
148
166
|
# Check every interval seconds to see if yield doesn't raise an exception.
|
149
167
|
# Give up after timeout seconds.
|
@@ -155,17 +173,31 @@ module Appium
|
|
155
173
|
# @param [String] message Exception message if timed out.
|
156
174
|
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
|
157
175
|
#
|
158
|
-
# @example
|
176
|
+
# @example With core instance
|
159
177
|
#
|
160
178
|
# @core.wait { @driver.find_element :accessibility_id, 'something' }
|
179
|
+
# @core.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
|
180
|
+
#
|
181
|
+
# @core.wait_until { @driver.find_element :accessibility_id, 'something' }
|
182
|
+
# @core.wait_until(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
|
183
|
+
#
|
184
|
+
# @example With driver instance
|
185
|
+
#
|
186
|
+
# @driver.wait { @driver.find_element :accessibility_id, 'something' }
|
187
|
+
# @driver.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
|
188
|
+
#
|
189
|
+
# @driver.wait_until { |d| d.find_element :accessibility_id, 'something' }
|
190
|
+
# @driver.wait_until(timeout: 30, interval: 2) { |d| d.find_element :accessibility_id, 'something' }
|
161
191
|
#
|
162
|
-
def
|
192
|
+
def wait_until(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
|
163
193
|
Wait.until(timeout: timeout || @wait_timeout,
|
164
194
|
interval: interval || @wait_interval,
|
165
195
|
message: message,
|
166
196
|
ignored: ignored,
|
167
|
-
object: self
|
197
|
+
object: self,
|
198
|
+
&block)
|
168
199
|
end
|
200
|
+
alias wait wait_until
|
169
201
|
end
|
170
202
|
end # module Core
|
171
203
|
end # module Appium
|
@@ -79,11 +79,7 @@ module Appium
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def create_bridge_command(method, &block)
|
82
|
-
::Appium::Core::Base::Bridge
|
83
|
-
undef_method method if method_defined? method
|
84
|
-
block_given? ? class_eval(&block) : define_method(method) { execute method }
|
85
|
-
end
|
86
|
-
::Appium::Core::Base::Bridge::W3C.class_eval do
|
82
|
+
::Appium::Core::Base::Bridge.class_eval do
|
87
83
|
undef_method method if method_defined? method
|
88
84
|
block_given? ? class_eval(&block) : define_method(method) { execute method }
|
89
85
|
end
|
@@ -27,6 +27,8 @@ module Appium
|
|
27
27
|
autoload :Xcuitest, 'appium_lib_core/ios_xcuitest'
|
28
28
|
end
|
29
29
|
|
30
|
+
autoload :Mac2, 'appium_lib_core/mac2'
|
31
|
+
|
30
32
|
autoload :Windows, 'appium_lib_core/windows'
|
31
33
|
|
32
34
|
# This options affects only client side as <code>:appium_lib</code> key.<br>
|
@@ -38,18 +40,18 @@ module Appium
|
|
38
40
|
|
39
41
|
def initialize(appium_lib_opts)
|
40
42
|
@custom_url = appium_lib_opts.fetch :server_url, nil
|
41
|
-
@default_wait = appium_lib_opts.fetch :wait,
|
43
|
+
@default_wait = appium_lib_opts.fetch :wait, nil
|
42
44
|
@enable_idempotency_header = appium_lib_opts.fetch :enable_idempotency_header, true
|
43
45
|
|
44
46
|
# bump current session id into a particular file
|
45
47
|
@export_session = appium_lib_opts.fetch :export_session, false
|
46
48
|
@export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session
|
47
49
|
|
48
|
-
@direct_connect = appium_lib_opts.fetch :direct_connect,
|
50
|
+
@direct_connect = appium_lib_opts.fetch :direct_connect, true
|
49
51
|
|
50
52
|
@port = appium_lib_opts.fetch :port, Driver::DEFAULT_APPIUM_PORT
|
51
53
|
|
52
|
-
# timeout and interval used in ::Appium::
|
54
|
+
# timeout and interval used in ::Appium::Commn.wait/wait_true
|
53
55
|
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
|
54
56
|
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
|
55
57
|
|
@@ -74,6 +76,13 @@ module Appium
|
|
74
76
|
path: 'directConnectPath'
|
75
77
|
}.freeze
|
76
78
|
|
79
|
+
W3C_KEYS = {
|
80
|
+
protocol: 'appium:directConnectProtocol',
|
81
|
+
host: 'appium:directConnectHost',
|
82
|
+
port: 'appium:directConnectPort',
|
83
|
+
path: 'appium:directConnectPath'
|
84
|
+
}.freeze
|
85
|
+
|
77
86
|
# @return [string] Returns a protocol such as http/https
|
78
87
|
attr_reader :protocol
|
79
88
|
|
@@ -87,10 +96,10 @@ module Appium
|
|
87
96
|
attr_reader :path
|
88
97
|
|
89
98
|
def initialize(capabilities)
|
90
|
-
@protocol = capabilities[KEYS[:protocol]]
|
91
|
-
@host = capabilities[KEYS[:host]]
|
92
|
-
@port = capabilities[KEYS[:port]]
|
93
|
-
@path = capabilities[KEYS[:path]]
|
99
|
+
@protocol = capabilities[W3C_KEYS[:protocol]] || capabilities[KEYS[:protocol]]
|
100
|
+
@host = capabilities[W3C_KEYS[:host]] || capabilities[KEYS[:host]]
|
101
|
+
@port = capabilities[W3C_KEYS[:port]] || capabilities[KEYS[:port]]
|
102
|
+
@path = capabilities[W3C_KEYS[:path]] || capabilities[KEYS[:path]]
|
94
103
|
end
|
95
104
|
end
|
96
105
|
|
@@ -133,11 +142,9 @@ module Appium
|
|
133
142
|
attr_reader :export_session_path
|
134
143
|
|
135
144
|
# Default wait time for elements to appear in Appium server side.
|
136
|
-
# Defaults to {::Appium::Core::Driver::DEFAULT_IMPLICIT_WAIT}.<br>
|
137
145
|
# Provide <code>{ appium_lib: { wait: 30 } }</code> to {::Appium::Core.for}
|
138
146
|
# @return [Integer]
|
139
147
|
attr_reader :default_wait
|
140
|
-
DEFAULT_IMPLICIT_WAIT = 0
|
141
148
|
|
142
149
|
# Appium's server port. 4723 is by default. Defaults to {::Appium::Core::Driver::DEFAULT_APPIUM_PORT}.<br>
|
143
150
|
# Provide <code>{ appium_lib: { port: 8080 } }</code> to {::Appium::Core.for}.
|
@@ -175,7 +182,8 @@ module Appium
|
|
175
182
|
# - <code>directConnectPort</code>
|
176
183
|
# - <code>directConnectPath</code>
|
177
184
|
#
|
178
|
-
#
|
185
|
+
# ignore them if this parameter is <code>false</code>. Defaults to true.
|
186
|
+
# These keys can have <code>appium:</code> prefix.
|
179
187
|
#
|
180
188
|
# @return [Bool]
|
181
189
|
attr_reader :direct_connect
|
@@ -185,8 +193,6 @@ module Appium
|
|
185
193
|
# @option opts [Hash] :caps Appium capabilities.
|
186
194
|
# @option opts [Hash] :capabilities The same as :caps.
|
187
195
|
# 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
196
|
# @option opts [Appium::Core::Options] :appium_lib Capabilities affect only ruby client
|
191
197
|
# @option opts [String] :url The same as :custom_url in :appium_lib.
|
192
198
|
# This param is for compatibility with Selenium WebDriver format
|
@@ -197,10 +203,8 @@ module Appium
|
|
197
203
|
#
|
198
204
|
# # format 1
|
199
205
|
# @core = Appium::Core.for caps: {...}, appium_lib: {...}
|
200
|
-
# # format 2. 'capabilities:'
|
206
|
+
# # format 2. 'capabilities:' is also available instead of 'caps:'.
|
201
207
|
# @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
208
|
#
|
205
209
|
#
|
206
210
|
# require 'rubygems'
|
@@ -228,7 +232,7 @@ module Appium
|
|
228
232
|
# @core.start_driver # Connect to 'http://127.0.0.1:8080/wd/hub' because of 'port: 8080'
|
229
233
|
#
|
230
234
|
# # Start iOS driver with .zip file over HTTP
|
231
|
-
# #
|
235
|
+
# # 'capabilities:' is also available instead of 'caps:'. Either is fine.
|
232
236
|
# opts = {
|
233
237
|
# capabilities: {
|
234
238
|
# platformName: :ios,
|
@@ -252,7 +256,7 @@ module Appium
|
|
252
256
|
# # Start iOS driver as another format. 'url' is available like below
|
253
257
|
# opts = {
|
254
258
|
# url: "http://custom-host:8080/wd/hub.com",
|
255
|
-
#
|
259
|
+
# capabilities: {
|
256
260
|
# platformName: :ios,
|
257
261
|
# platformVersion: '11.0',
|
258
262
|
# deviceName: 'iPhone Simulator',
|
@@ -271,7 +275,40 @@ module Appium
|
|
271
275
|
# @core.start_driver # start driver with 'url'. Connect to 'http://custom-host:8080/wd/hub.com'
|
272
276
|
#
|
273
277
|
def self.for(opts = {})
|
274
|
-
new(opts)
|
278
|
+
new.setup_for_new_session(opts)
|
279
|
+
end
|
280
|
+
|
281
|
+
# Attach to an existing session. The main usage of this method is to attach to
|
282
|
+
# an existing session for debugging. The generated driver instance has the capabilities which
|
283
|
+
# has the given automationName and platformName only since the W3C WebDriver spec does not provide
|
284
|
+
# an endpoint to get running session's capabilities.
|
285
|
+
#
|
286
|
+
#
|
287
|
+
# @param [String] The session id to attach to.
|
288
|
+
# @param [String] url The WebDriver URL to attach to with the session_id.
|
289
|
+
# @param [String] automation_name The platform name to keep in the dummy capabilities
|
290
|
+
# @param [String] platform_name The automation name to keep in the dummy capabilities
|
291
|
+
# @return [Selenium::WebDriver] A new driver instance with the given session id.
|
292
|
+
#
|
293
|
+
# @example
|
294
|
+
#
|
295
|
+
# new_driver = ::Appium::Core::Driver.attach_to(
|
296
|
+
# driver.session_id, # The 'driver' has an existing session id
|
297
|
+
# url: 'http://127.0.0.1:4723/wd/hub', automation_name: 'UiAutomator2', platform_name: 'Android'
|
298
|
+
# )
|
299
|
+
# new_driver.page_source # for example
|
300
|
+
#
|
301
|
+
def self.attach_to(
|
302
|
+
session_id, url: nil, automation_name: nil, platform_name: nil,
|
303
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }
|
304
|
+
)
|
305
|
+
new.attach_to(
|
306
|
+
session_id,
|
307
|
+
automation_name: automation_name,
|
308
|
+
platform_name: platform_name,
|
309
|
+
url: url,
|
310
|
+
http_client_ops: http_client_ops
|
311
|
+
)
|
275
312
|
end
|
276
313
|
|
277
314
|
private
|
@@ -282,17 +319,25 @@ module Appium
|
|
282
319
|
@delegate_target
|
283
320
|
end
|
284
321
|
|
285
|
-
public
|
286
|
-
|
287
322
|
# @private
|
288
|
-
def initialize
|
323
|
+
def initialize
|
289
324
|
@delegate_target = self # for testing purpose
|
290
325
|
@automation_name = nil # initialise before 'set_automation_name'
|
326
|
+
end
|
327
|
+
|
328
|
+
public
|
291
329
|
|
292
|
-
|
293
|
-
|
330
|
+
# @private
|
331
|
+
# Set up for a neww session
|
332
|
+
def setup_for_new_session(opts = {})
|
333
|
+
@custom_url = opts.delete :url # to set the custom url as :url
|
334
|
+
|
335
|
+
# TODO: Remove when we implement Options
|
336
|
+
# The symbolize_keys is to keep compatiility for the legacy code, which allows capabilities to give 'string' as the key.
|
337
|
+
# The toplevel `caps`, `capabilities` and `appium_lib` are expected to be symbol.
|
338
|
+
# FIXME: First, please try to remove `nested: true` to `nested: false`.
|
339
|
+
opts = Appium.symbolize_keys(opts, nested: true)
|
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',
|
@@ -365,11 +409,12 @@ module Appium
|
|
365
409
|
end
|
366
410
|
|
367
411
|
begin
|
368
|
-
|
369
|
-
|
370
|
-
|
412
|
+
@driver = ::Appium::Core::Base::Driver.new(listener: @listener,
|
413
|
+
http_client: @http_client,
|
414
|
+
capabilities: @caps, # ::Appium::Core::Base::Capabilities
|
371
415
|
url: @custom_url,
|
372
|
-
|
416
|
+
wait_timeout: @wait_timeout,
|
417
|
+
wait_interval: @wait_interval)
|
373
418
|
|
374
419
|
if @direct_connect
|
375
420
|
d_c = DirectConnections.new(@driver.capabilities)
|
@@ -387,6 +432,8 @@ module Appium
|
|
387
432
|
@http_client.additional_headers.delete Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
|
388
433
|
end
|
389
434
|
|
435
|
+
# TODO: this method can be removed after releasing Appium 2.0, and after a while
|
436
|
+
# since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
|
390
437
|
# If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
|
391
438
|
# Since @automation_name is set only client side before start_driver is called.
|
392
439
|
set_automation_name_if_nil
|
@@ -396,7 +443,47 @@ module Appium
|
|
396
443
|
@driver
|
397
444
|
end
|
398
445
|
|
399
|
-
|
446
|
+
# @privvate
|
447
|
+
# Attach to an existing session
|
448
|
+
def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
|
449
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
450
|
+
|
451
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
|
452
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
|
453
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
|
454
|
+
|
455
|
+
@custom_url = url
|
456
|
+
|
457
|
+
# use lowercase internally
|
458
|
+
@automation_name = convert_downcase(automation_name)
|
459
|
+
@device = convert_downcase(platform_name)
|
460
|
+
|
461
|
+
extend_for(device: @device, automation_name: @automation_name)
|
462
|
+
|
463
|
+
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
464
|
+
open_timeout: http_client_ops.delete(:open_timeout),
|
465
|
+
read_timeout: http_client_ops.delete(:read_timeout)
|
466
|
+
|
467
|
+
# Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
|
468
|
+
# the new session request, this it does not needed.
|
469
|
+
|
470
|
+
begin
|
471
|
+
# included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
|
472
|
+
@driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
|
473
|
+
url: @custom_url,
|
474
|
+
listener: @listener,
|
475
|
+
existing_session_id: session_id,
|
476
|
+
automation_name: automation_name,
|
477
|
+
platform_name: platform_name)
|
478
|
+
|
479
|
+
# export session
|
480
|
+
write_session_id(@driver.session_id, @export_session_path) if @export_session
|
481
|
+
rescue Errno::ECONNREFUSED
|
482
|
+
raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
483
|
+
end
|
484
|
+
|
485
|
+
@driver
|
486
|
+
end
|
400
487
|
|
401
488
|
def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
|
402
489
|
client = http_client || Appium::Core::Base::Http::Default.new
|
@@ -410,6 +497,8 @@ module Appium
|
|
410
497
|
|
411
498
|
# Ignore setting default wait if the target driver has no implementation
|
412
499
|
def set_implicit_wait_by_default(wait)
|
500
|
+
return if @default_wait.nil?
|
501
|
+
|
413
502
|
@driver.manage.timeouts.implicit_wait = wait
|
414
503
|
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
415
504
|
unless e.message.include?('The operation requested is not yet implemented')
|
@@ -420,8 +509,6 @@ module Appium
|
|
420
509
|
{}
|
421
510
|
end
|
422
511
|
|
423
|
-
public
|
424
|
-
|
425
512
|
# Quits the driver
|
426
513
|
# @return [void]
|
427
514
|
#
|
@@ -435,7 +522,8 @@ module Appium
|
|
435
522
|
nil
|
436
523
|
end
|
437
524
|
|
438
|
-
# Returns the server's version info
|
525
|
+
# Returns the server's version info. This method calls +driver.remote_status+ internally
|
526
|
+
#
|
439
527
|
# @return [Hash]
|
440
528
|
#
|
441
529
|
# @example
|
@@ -449,18 +537,20 @@ module Appium
|
|
449
537
|
# }
|
450
538
|
# }
|
451
539
|
#
|
452
|
-
# Returns blank hash
|
540
|
+
# Returns blank hash in a case +driver.remote_status+ got an error
|
541
|
+
# such as Selenium Grid. It returns 500 error against 'remote_status'.
|
453
542
|
#
|
454
543
|
# @example
|
455
544
|
#
|
456
545
|
# @core.appium_server_version #=> {}
|
457
546
|
#
|
458
547
|
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')
|
548
|
+
return {} if @driver.nil?
|
462
549
|
|
463
|
-
|
550
|
+
@driver.remote_status
|
551
|
+
rescue StandardError
|
552
|
+
# Ignore error case in a case the target appium server
|
553
|
+
# does not support `/status` API.
|
464
554
|
{}
|
465
555
|
end
|
466
556
|
|
@@ -476,31 +566,26 @@ module Appium
|
|
476
566
|
p_version.split('.').map(&:to_i)
|
477
567
|
end
|
478
568
|
|
479
|
-
# Takes a png screenshot and saves to the target path.
|
480
|
-
#
|
481
|
-
# @param png_save_path [String] the full path to save the png
|
482
|
-
# @return [File]
|
483
|
-
#
|
484
|
-
# @example
|
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
|
492
|
-
end
|
493
|
-
|
494
569
|
private
|
495
570
|
|
571
|
+
def convert_to_symbol(value)
|
572
|
+
if value.nil?
|
573
|
+
value
|
574
|
+
else
|
575
|
+
value.to_sym
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
496
579
|
# @private
|
497
580
|
def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
|
498
581
|
extend Appium::Core
|
499
582
|
extend Appium::Core::Device
|
500
583
|
|
501
|
-
|
584
|
+
sym_automation_name = convert_to_symbol(automation_name)
|
585
|
+
|
586
|
+
case convert_to_symbol(device)
|
502
587
|
when :android
|
503
|
-
case
|
588
|
+
case sym_automation_name
|
504
589
|
when :espresso
|
505
590
|
::Appium::Core::Android::Espresso::Bridge.for self
|
506
591
|
when :uiautomator2
|
@@ -511,7 +596,7 @@ module Appium
|
|
511
596
|
::Appium::Core::Android::Uiautomator1::Bridge.for self
|
512
597
|
end
|
513
598
|
when :ios, :tvos
|
514
|
-
case
|
599
|
+
case sym_automation_name
|
515
600
|
when :safari
|
516
601
|
::Appium::Logger.debug('SafariDriver for iOS')
|
517
602
|
when :xcuitest
|
@@ -520,19 +605,19 @@ module Appium
|
|
520
605
|
::Appium::Core::Ios::Uiautomation::Bridge.for self
|
521
606
|
end
|
522
607
|
when :mac
|
523
|
-
case
|
608
|
+
case sym_automation_name
|
524
609
|
when :safari
|
525
610
|
::Appium::Logger.debug('SafariDriver for macOS')
|
526
611
|
when :gecko
|
527
612
|
::Appium::Logger.debug('Gecko Driver for macOS')
|
528
613
|
when :mac2
|
529
|
-
::Appium::
|
614
|
+
::Appium::Core::Mac2::Bridge.for self
|
530
615
|
else
|
531
616
|
# no Mac specific extentions
|
532
617
|
::Appium::Logger.debug('macOS Native')
|
533
618
|
end
|
534
619
|
when :windows
|
535
|
-
case
|
620
|
+
case sym_automation_name
|
536
621
|
when :gecko
|
537
622
|
::Appium::Logger.debug('Gecko Driver for Windows')
|
538
623
|
else
|
@@ -542,7 +627,7 @@ module Appium
|
|
542
627
|
# https://github.com/Samsung/appium-tizen-driver
|
543
628
|
::Appium::Logger.debug('tizen')
|
544
629
|
else
|
545
|
-
case
|
630
|
+
case sym_automation_name
|
546
631
|
when :youiengine
|
547
632
|
# https://github.com/YOU-i-Labs/appium-youiengine-driver
|
548
633
|
::Appium::Logger.debug('YouiEngine')
|
@@ -559,36 +644,9 @@ module Appium
|
|
559
644
|
self
|
560
645
|
end
|
561
646
|
|
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
647
|
# @private
|
589
648
|
def get_caps(opts)
|
590
|
-
|
591
|
-
Core::Base::Capabilities.create_capabilities(opts[:caps] || opts[:capabilities] || opts[:desired_capabilities] || {})
|
649
|
+
Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
|
592
650
|
end
|
593
651
|
|
594
652
|
# @private
|
@@ -601,6 +659,7 @@ module Appium
|
|
601
659
|
# The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
|
602
660
|
# Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
|
603
661
|
def set_app_path
|
662
|
+
# FIXME: maybe `:app` should check `app` as well.
|
604
663
|
return unless @caps && @caps[:app] && !@caps[:app].empty?
|
605
664
|
return if @caps[:app] =~ URI::DEFAULT_PARSER.make_regexp
|
606
665
|
|
@@ -639,18 +698,24 @@ module Appium
|
|
639
698
|
# @private
|
640
699
|
def set_appium_device
|
641
700
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
642
|
-
|
701
|
+
# TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
|
702
|
+
@device = @caps[:platformName] || @caps['platformName']
|
643
703
|
return @device unless @device
|
644
704
|
|
645
|
-
@device =
|
705
|
+
@device = convert_downcase @device
|
646
706
|
end
|
647
707
|
|
648
708
|
# @private
|
649
709
|
def set_automation_name
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
710
|
+
# TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
|
711
|
+
candidate = @caps[:automationName] || @caps['automationName']
|
712
|
+
@automation_name = candidate if candidate
|
713
|
+
@automation_name = convert_downcase @automation_name if @automation_name
|
714
|
+
end
|
715
|
+
|
716
|
+
# @private
|
717
|
+
def convert_downcase(value)
|
718
|
+
value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
|
654
719
|
end
|
655
720
|
|
656
721
|
# @private
|
@@ -664,6 +729,10 @@ module Appium
|
|
664
729
|
|
665
730
|
# @private
|
666
731
|
def write_session_id(session_id, export_path = '/tmp/appium_lib_session')
|
732
|
+
::Appium::Logger.warn(
|
733
|
+
'[DEPRECATION] export_session option will be removed. ' \
|
734
|
+
'Please save the session id by yourself with #session_id method like @driver.session_id.'
|
735
|
+
)
|
667
736
|
export_path = export_path.tr('/', '\\') if ::Appium::Core::Base.platform.windows?
|
668
737
|
File.write(export_path, session_id)
|
669
738
|
rescue IOError => e
|