appium_lib_core 4.1.0 → 6.2.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 +207 -272
- data/README.md +41 -14
- 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 +311 -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/{ios/uiautomation/bridge.rb → common/base/remote_status.rb} +9 -8
- 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 -6
- data/lib/appium_lib_core/common/base.rb +1 -3
- data/lib/appium_lib_core/common/command.rb +259 -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 -8
- 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 +177 -102
- data/lib/appium_lib_core/{patch.rb → element.rb} +66 -9
- 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/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 +27 -83
- 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/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
@@ -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
|
@@ -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,10 +26,11 @@ 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>
|
@@ -38,18 +42,18 @@ module Appium
|
|
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
48
|
# bump current session id into a particular file
|
45
49
|
@export_session = appium_lib_opts.fetch :export_session, false
|
46
50
|
@export_session_path = appium_lib_opts.fetch :export_session_path, default_tmp_appium_lib_session
|
47
51
|
|
48
|
-
@direct_connect = appium_lib_opts.fetch :direct_connect,
|
52
|
+
@direct_connect = appium_lib_opts.fetch :direct_connect, true
|
49
53
|
|
50
54
|
@port = appium_lib_opts.fetch :port, Driver::DEFAULT_APPIUM_PORT
|
51
55
|
|
52
|
-
# timeout and interval used in ::Appium::
|
56
|
+
# timeout and interval used in ::Appium::Commn.wait/wait_true
|
53
57
|
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
|
54
58
|
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
|
55
59
|
|
@@ -74,6 +78,13 @@ module Appium
|
|
74
78
|
path: 'directConnectPath'
|
75
79
|
}.freeze
|
76
80
|
|
81
|
+
W3C_KEYS = {
|
82
|
+
protocol: 'appium:directConnectProtocol',
|
83
|
+
host: 'appium:directConnectHost',
|
84
|
+
port: 'appium:directConnectPort',
|
85
|
+
path: 'appium:directConnectPath'
|
86
|
+
}.freeze
|
87
|
+
|
77
88
|
# @return [string] Returns a protocol such as http/https
|
78
89
|
attr_reader :protocol
|
79
90
|
|
@@ -87,10 +98,10 @@ module Appium
|
|
87
98
|
attr_reader :path
|
88
99
|
|
89
100
|
def initialize(capabilities)
|
90
|
-
@protocol = capabilities[KEYS[:protocol]]
|
91
|
-
@host = capabilities[KEYS[:host]]
|
92
|
-
@port = capabilities[KEYS[:port]]
|
93
|
-
@path = capabilities[KEYS[:path]]
|
101
|
+
@protocol = capabilities[W3C_KEYS[:protocol]] || capabilities[KEYS[:protocol]]
|
102
|
+
@host = capabilities[W3C_KEYS[:host]] || capabilities[KEYS[:host]]
|
103
|
+
@port = capabilities[W3C_KEYS[:port]] || capabilities[KEYS[:port]]
|
104
|
+
@path = capabilities[W3C_KEYS[:path]] || capabilities[KEYS[:path]]
|
94
105
|
end
|
95
106
|
end
|
96
107
|
|
@@ -133,11 +144,9 @@ module Appium
|
|
133
144
|
attr_reader :export_session_path
|
134
145
|
|
135
146
|
# Default wait time for elements to appear in Appium server side.
|
136
|
-
# Defaults to {::Appium::Core::Driver::DEFAULT_IMPLICIT_WAIT}.<br>
|
137
147
|
# Provide <code>{ appium_lib: { wait: 30 } }</code> to {::Appium::Core.for}
|
138
148
|
# @return [Integer]
|
139
149
|
attr_reader :default_wait
|
140
|
-
DEFAULT_IMPLICIT_WAIT = 0
|
141
150
|
|
142
151
|
# Appium's server port. 4723 is by default. Defaults to {::Appium::Core::Driver::DEFAULT_APPIUM_PORT}.<br>
|
143
152
|
# Provide <code>{ appium_lib: { port: 8080 } }</code> to {::Appium::Core.for}.
|
@@ -175,7 +184,8 @@ module Appium
|
|
175
184
|
# - <code>directConnectPort</code>
|
176
185
|
# - <code>directConnectPath</code>
|
177
186
|
#
|
178
|
-
#
|
187
|
+
# ignore them if this parameter is <code>false</code>. Defaults to true.
|
188
|
+
# These keys can have <code>appium:</code> prefix.
|
179
189
|
#
|
180
190
|
# @return [Bool]
|
181
191
|
attr_reader :direct_connect
|
@@ -185,8 +195,6 @@ module Appium
|
|
185
195
|
# @option opts [Hash] :caps Appium capabilities.
|
186
196
|
# @option opts [Hash] :capabilities The same as :caps.
|
187
197
|
# 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
198
|
# @option opts [Appium::Core::Options] :appium_lib Capabilities affect only ruby client
|
191
199
|
# @option opts [String] :url The same as :custom_url in :appium_lib.
|
192
200
|
# This param is for compatibility with Selenium WebDriver format
|
@@ -197,10 +205,8 @@ module Appium
|
|
197
205
|
#
|
198
206
|
# # format 1
|
199
207
|
# @core = Appium::Core.for caps: {...}, appium_lib: {...}
|
200
|
-
# # format 2. 'capabilities:'
|
208
|
+
# # format 2. 'capabilities:' is also available instead of 'caps:'.
|
201
209
|
# @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
210
|
#
|
205
211
|
#
|
206
212
|
# require 'rubygems'
|
@@ -228,7 +234,7 @@ module Appium
|
|
228
234
|
# @core.start_driver # Connect to 'http://127.0.0.1:8080/wd/hub' because of 'port: 8080'
|
229
235
|
#
|
230
236
|
# # Start iOS driver with .zip file over HTTP
|
231
|
-
# #
|
237
|
+
# # 'capabilities:' is also available instead of 'caps:'. Either is fine.
|
232
238
|
# opts = {
|
233
239
|
# capabilities: {
|
234
240
|
# platformName: :ios,
|
@@ -252,7 +258,7 @@ module Appium
|
|
252
258
|
# # Start iOS driver as another format. 'url' is available like below
|
253
259
|
# opts = {
|
254
260
|
# url: "http://custom-host:8080/wd/hub.com",
|
255
|
-
#
|
261
|
+
# capabilities: {
|
256
262
|
# platformName: :ios,
|
257
263
|
# platformVersion: '11.0',
|
258
264
|
# deviceName: 'iPhone Simulator',
|
@@ -271,7 +277,40 @@ module Appium
|
|
271
277
|
# @core.start_driver # start driver with 'url'. Connect to 'http://custom-host:8080/wd/hub.com'
|
272
278
|
#
|
273
279
|
def self.for(opts = {})
|
274
|
-
new(opts)
|
280
|
+
new.setup_for_new_session(opts)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Attach to an existing session. The main usage of this method is to attach to
|
284
|
+
# an existing session for debugging. The generated driver instance has the capabilities which
|
285
|
+
# has the given automationName and platformName only since the W3C WebDriver spec does not provide
|
286
|
+
# an endpoint to get running session's capabilities.
|
287
|
+
#
|
288
|
+
#
|
289
|
+
# @param [String] The session id to attach to.
|
290
|
+
# @param [String] url The WebDriver URL to attach to with the session_id.
|
291
|
+
# @param [String] automation_name The platform name to keep in the dummy capabilities
|
292
|
+
# @param [String] platform_name The automation name to keep in the dummy capabilities
|
293
|
+
# @return [Selenium::WebDriver] A new driver instance with the given session id.
|
294
|
+
#
|
295
|
+
# @example
|
296
|
+
#
|
297
|
+
# new_driver = ::Appium::Core::Driver.attach_to(
|
298
|
+
# driver.session_id, # The 'driver' has an existing session id
|
299
|
+
# url: 'http://127.0.0.1:4723/wd/hub', automation_name: 'UiAutomator2', platform_name: 'Android'
|
300
|
+
# )
|
301
|
+
# new_driver.page_source # for example
|
302
|
+
#
|
303
|
+
def self.attach_to(
|
304
|
+
session_id, url: nil, automation_name: nil, platform_name: nil,
|
305
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 }
|
306
|
+
)
|
307
|
+
new.attach_to(
|
308
|
+
session_id,
|
309
|
+
automation_name: automation_name,
|
310
|
+
platform_name: platform_name,
|
311
|
+
url: url,
|
312
|
+
http_client_ops: http_client_ops
|
313
|
+
)
|
275
314
|
end
|
276
315
|
|
277
316
|
private
|
@@ -282,17 +321,25 @@ module Appium
|
|
282
321
|
@delegate_target
|
283
322
|
end
|
284
323
|
|
285
|
-
public
|
286
|
-
|
287
324
|
# @private
|
288
|
-
def initialize
|
325
|
+
def initialize
|
289
326
|
@delegate_target = self # for testing purpose
|
290
327
|
@automation_name = nil # initialise before 'set_automation_name'
|
328
|
+
end
|
291
329
|
|
292
|
-
|
293
|
-
|
330
|
+
public
|
331
|
+
|
332
|
+
# @private
|
333
|
+
# Set up for a new session
|
334
|
+
def setup_for_new_session(opts = {})
|
335
|
+
@custom_url = opts.delete :url # to set the custom url as :url
|
336
|
+
|
337
|
+
# TODO: Remove when we implement Options
|
338
|
+
# The symbolize_keys is to keep compatiility for the legacy code, which allows capabilities to give 'string' as the key.
|
339
|
+
# The toplevel `caps`, `capabilities` and `appium_lib` are expected to be symbol.
|
340
|
+
# FIXME: First, please try to remove `nested: true` to `nested: false`.
|
341
|
+
opts = Appium.symbolize_keys(opts, nested: true)
|
294
342
|
|
295
|
-
@custom_url = opts.delete :url
|
296
343
|
@caps = get_caps(opts)
|
297
344
|
|
298
345
|
set_appium_lib_specific_values(get_appium_lib_opts(opts))
|
@@ -301,8 +348,7 @@ module Appium
|
|
301
348
|
set_automation_name
|
302
349
|
|
303
350
|
extend_for(device: @device, automation_name: @automation_name)
|
304
|
-
|
305
|
-
self # rubocop:disable Lint/Void
|
351
|
+
self
|
306
352
|
end
|
307
353
|
|
308
354
|
# Creates a new global driver and quits the old one if it exists.
|
@@ -313,7 +359,7 @@ module Appium
|
|
313
359
|
# @option http_client_ops [Hash] :http_client Custom HTTP Client
|
314
360
|
# @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
|
315
361
|
# @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
|
316
|
-
# @return [Selenium::WebDriver]
|
362
|
+
# @return [Selenium::WebDriver] A new driver instance
|
317
363
|
#
|
318
364
|
# @example
|
319
365
|
#
|
@@ -324,7 +370,7 @@ module Appium
|
|
324
370
|
#
|
325
371
|
# # Start iOS driver
|
326
372
|
# opts = {
|
327
|
-
#
|
373
|
+
# capabilities: {
|
328
374
|
# platformName: :ios,
|
329
375
|
# platformVersion: '11.0',
|
330
376
|
# deviceName: 'iPhone Simulator',
|
@@ -365,11 +411,12 @@ module Appium
|
|
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)
|
@@ -387,6 +434,8 @@ module Appium
|
|
387
434
|
@http_client.additional_headers.delete Appium::Core::Base::Http::RequestHeaders::KEYS[:idempotency]
|
388
435
|
end
|
389
436
|
|
437
|
+
# TODO: this method can be removed after releasing Appium 2.0, and after a while
|
438
|
+
# since Appium 2.0 reuqires 'automationName'. This method won't help anymore then.
|
390
439
|
# If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
|
391
440
|
# Since @automation_name is set only client side before start_driver is called.
|
392
441
|
set_automation_name_if_nil
|
@@ -396,7 +445,47 @@ module Appium
|
|
396
445
|
@driver
|
397
446
|
end
|
398
447
|
|
399
|
-
private
|
448
|
+
# @private
|
449
|
+
# Attach to an existing session
|
450
|
+
def attach_to(session_id, url: nil, automation_name: nil, platform_name: nil,
|
451
|
+
http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
|
452
|
+
|
453
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :url must not be nil' if url.nil?
|
454
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :automation_name must not be nil' if automation_name.nil?
|
455
|
+
raise ::Appium::Core::Error::ArgumentError, 'The :platform_name must not be nil' if platform_name.nil?
|
456
|
+
|
457
|
+
@custom_url = url
|
458
|
+
|
459
|
+
# use lowercase internally
|
460
|
+
@automation_name = convert_downcase(automation_name)
|
461
|
+
@device = convert_downcase(platform_name)
|
462
|
+
|
463
|
+
extend_for(device: @device, automation_name: @automation_name)
|
464
|
+
|
465
|
+
@http_client = get_http_client http_client: http_client_ops.delete(:http_client),
|
466
|
+
open_timeout: http_client_ops.delete(:open_timeout),
|
467
|
+
read_timeout: http_client_ops.delete(:read_timeout)
|
468
|
+
|
469
|
+
# Note that 'enable_idempotency_header' works only a new session reqeust. The attach_to method skips
|
470
|
+
# the new session request, this it does not needed.
|
471
|
+
|
472
|
+
begin
|
473
|
+
# included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
|
474
|
+
@driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
|
475
|
+
url: @custom_url,
|
476
|
+
listener: @listener,
|
477
|
+
existing_session_id: session_id,
|
478
|
+
automation_name: automation_name,
|
479
|
+
platform_name: platform_name)
|
480
|
+
|
481
|
+
# export session
|
482
|
+
write_session_id(@driver.session_id, @export_session_path) if @export_session
|
483
|
+
rescue Errno::ECONNREFUSED
|
484
|
+
raise "ERROR: Unable to connect to Appium. Is the server running on #{@custom_url}?"
|
485
|
+
end
|
486
|
+
|
487
|
+
@driver
|
488
|
+
end
|
400
489
|
|
401
490
|
def get_http_client(http_client: nil, open_timeout: nil, read_timeout: nil)
|
402
491
|
client = http_client || Appium::Core::Base::Http::Default.new
|
@@ -410,6 +499,8 @@ module Appium
|
|
410
499
|
|
411
500
|
# Ignore setting default wait if the target driver has no implementation
|
412
501
|
def set_implicit_wait_by_default(wait)
|
502
|
+
return if @default_wait.nil?
|
503
|
+
|
413
504
|
@driver.manage.timeouts.implicit_wait = wait
|
414
505
|
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
415
506
|
unless e.message.include?('The operation requested is not yet implemented')
|
@@ -420,9 +511,7 @@ module Appium
|
|
420
511
|
{}
|
421
512
|
end
|
422
513
|
|
423
|
-
|
424
|
-
|
425
|
-
# Quits the driver
|
514
|
+
# [Deprecated] Quits the driver. This method is the same as @driver.quit
|
426
515
|
# @return [void]
|
427
516
|
#
|
428
517
|
# @example
|
@@ -430,12 +519,14 @@ module Appium
|
|
430
519
|
# @core.quit_driver
|
431
520
|
#
|
432
521
|
def quit_driver
|
522
|
+
::Appium::Logger.warn('[DEPRECATION] quit_driver will be removed. Please use @driver.quit instead.')
|
433
523
|
@driver.quit
|
434
524
|
rescue # rubocop:disable Style/RescueStandardError
|
435
525
|
nil
|
436
526
|
end
|
437
527
|
|
438
|
-
# Returns the server's version info
|
528
|
+
# Returns the server's version info. This method calls +driver.remote_status+ internally
|
529
|
+
#
|
439
530
|
# @return [Hash]
|
440
531
|
#
|
441
532
|
# @example
|
@@ -449,18 +540,20 @@ module Appium
|
|
449
540
|
# }
|
450
541
|
# }
|
451
542
|
#
|
452
|
-
# Returns blank hash
|
543
|
+
# Returns blank hash in a case +driver.remote_status+ got an error
|
544
|
+
# such as Selenium Grid. It returns 500 error against 'remote_status'.
|
453
545
|
#
|
454
546
|
# @example
|
455
547
|
#
|
456
548
|
# @core.appium_server_version #=> {}
|
457
549
|
#
|
458
550
|
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')
|
551
|
+
return {} if @driver.nil?
|
462
552
|
|
463
|
-
|
553
|
+
@driver.remote_status
|
554
|
+
rescue StandardError
|
555
|
+
# Ignore error case in a case the target appium server
|
556
|
+
# does not support `/status` API.
|
464
557
|
{}
|
465
558
|
end
|
466
559
|
|
@@ -472,35 +565,35 @@ module Appium
|
|
472
565
|
# @core.platform_version #=> [10,1,1]
|
473
566
|
#
|
474
567
|
def platform_version
|
568
|
+
::Appium::Logger.warn(
|
569
|
+
'[DEPRECATION] platform_version method will be. ' \
|
570
|
+
'Please check the platformVersion via @driver.capabilities["platformVersion"] instead.'
|
571
|
+
)
|
572
|
+
|
475
573
|
p_version = @driver.capabilities['platformVersion'] || @driver.session_capabilities['platformVersion']
|
476
574
|
p_version.split('.').map(&:to_i)
|
477
575
|
end
|
478
576
|
|
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
577
|
private
|
495
578
|
|
579
|
+
def convert_to_symbol(value)
|
580
|
+
if value.nil?
|
581
|
+
value
|
582
|
+
else
|
583
|
+
value.to_sym
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
496
587
|
# @private
|
497
588
|
def extend_for(device:, automation_name:) # rubocop:disable Metrics/CyclomaticComplexity
|
498
589
|
extend Appium::Core
|
499
590
|
extend Appium::Core::Device
|
500
591
|
|
501
|
-
|
592
|
+
sym_automation_name = convert_to_symbol(automation_name)
|
593
|
+
|
594
|
+
case convert_to_symbol(device)
|
502
595
|
when :android
|
503
|
-
case
|
596
|
+
case sym_automation_name
|
504
597
|
when :espresso
|
505
598
|
::Appium::Core::Android::Espresso::Bridge.for self
|
506
599
|
when :uiautomator2
|
@@ -511,28 +604,26 @@ module Appium
|
|
511
604
|
::Appium::Core::Android::Uiautomator1::Bridge.for self
|
512
605
|
end
|
513
606
|
when :ios, :tvos
|
514
|
-
case
|
607
|
+
case sym_automation_name
|
515
608
|
when :safari
|
516
609
|
::Appium::Logger.debug('SafariDriver for iOS')
|
517
|
-
|
610
|
+
else # XCUITest
|
518
611
|
::Appium::Core::Ios::Xcuitest::Bridge.for self
|
519
|
-
else # default and UIAutomation
|
520
|
-
::Appium::Core::Ios::Uiautomation::Bridge.for self
|
521
612
|
end
|
522
613
|
when :mac
|
523
|
-
case
|
614
|
+
case sym_automation_name
|
524
615
|
when :safari
|
525
616
|
::Appium::Logger.debug('SafariDriver for macOS')
|
526
617
|
when :gecko
|
527
618
|
::Appium::Logger.debug('Gecko Driver for macOS')
|
528
619
|
when :mac2
|
529
|
-
::Appium::
|
620
|
+
::Appium::Core::Mac2::Bridge.for self
|
530
621
|
else
|
531
622
|
# no Mac specific extentions
|
532
623
|
::Appium::Logger.debug('macOS Native')
|
533
624
|
end
|
534
625
|
when :windows
|
535
|
-
case
|
626
|
+
case sym_automation_name
|
536
627
|
when :gecko
|
537
628
|
::Appium::Logger.debug('Gecko Driver for Windows')
|
538
629
|
else
|
@@ -542,7 +633,7 @@ module Appium
|
|
542
633
|
# https://github.com/Samsung/appium-tizen-driver
|
543
634
|
::Appium::Logger.debug('tizen')
|
544
635
|
else
|
545
|
-
case
|
636
|
+
case sym_automation_name
|
546
637
|
when :youiengine
|
547
638
|
# https://github.com/YOU-i-Labs/appium-youiengine-driver
|
548
639
|
::Appium::Logger.debug('YouiEngine')
|
@@ -559,36 +650,9 @@ module Appium
|
|
559
650
|
self
|
560
651
|
end
|
561
652
|
|
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
653
|
# @private
|
589
654
|
def get_caps(opts)
|
590
|
-
|
591
|
-
Core::Base::Capabilities.create_capabilities(opts[:caps] || opts[:capabilities] || opts[:desired_capabilities] || {})
|
655
|
+
Core::Base::Capabilities.new(opts[:caps] || opts[:capabilities] || {})
|
592
656
|
end
|
593
657
|
|
594
658
|
# @private
|
@@ -601,6 +665,7 @@ module Appium
|
|
601
665
|
# The path can be local, HTTP/S, Windows Share and other path like 'sauce-storage:'.
|
602
666
|
# Use @caps[:app] without modifications if the path isn't HTTP/S or local path.
|
603
667
|
def set_app_path
|
668
|
+
# FIXME: maybe `:app` should check `app` as well.
|
604
669
|
return unless @caps && @caps[:app] && !@caps[:app].empty?
|
605
670
|
return if @caps[:app] =~ URI::DEFAULT_PARSER.make_regexp
|
606
671
|
|
@@ -639,18 +704,24 @@ module Appium
|
|
639
704
|
# @private
|
640
705
|
def set_appium_device
|
641
706
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
642
|
-
|
707
|
+
# TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
|
708
|
+
@device = @caps[:platformName] || @caps['platformName']
|
643
709
|
return @device unless @device
|
644
710
|
|
645
|
-
@device =
|
711
|
+
@device = convert_downcase @device
|
646
712
|
end
|
647
713
|
|
648
714
|
# @private
|
649
715
|
def set_automation_name
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
716
|
+
# TODO: check if the Appium.symbolize_keys(opts, nested: false) enoug with this
|
717
|
+
candidate = @caps[:automationName] || @caps['automationName']
|
718
|
+
@automation_name = candidate if candidate
|
719
|
+
@automation_name = convert_downcase @automation_name if @automation_name
|
720
|
+
end
|
721
|
+
|
722
|
+
# @private
|
723
|
+
def convert_downcase(value)
|
724
|
+
value.is_a?(Symbol) ? value.downcase : value.downcase.strip.intern
|
654
725
|
end
|
655
726
|
|
656
727
|
# @private
|
@@ -664,6 +735,10 @@ module Appium
|
|
664
735
|
|
665
736
|
# @private
|
666
737
|
def write_session_id(session_id, export_path = '/tmp/appium_lib_session')
|
738
|
+
::Appium::Logger.warn(
|
739
|
+
'[DEPRECATION] export_session option will be removed. ' \
|
740
|
+
'Please save the session id by yourself with #session_id method like @driver.session_id.'
|
741
|
+
)
|
667
742
|
export_path = export_path.tr('/', '\\') if ::Appium::Core::Base.platform.windows?
|
668
743
|
File.write(export_path, session_id)
|
669
744
|
rescue IOError => e
|