appium_lib_core 4.1.0 → 5.5.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 +158 -275
- data/README.md +13 -8
- 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 +291 -92
- 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 +225 -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 +2 -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 -1
- 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 +41 -75
- 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.rb +2 -5
- metadata +29 -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
data/README.md
CHANGED
@@ -10,7 +10,12 @@ This library wraps [selenium-webdriver](https://github.com/SeleniumHQ/selenium/w
|
|
10
10
|
|
11
11
|
# Documentation
|
12
12
|
|
13
|
-
-
|
13
|
+
- https://www.rubydoc.info/gems/appium_lib_core
|
14
|
+
- You can find working API examples in test code, [test/functional](test/functional)
|
15
|
+
|
16
|
+
# Migration from v4 to v5
|
17
|
+
|
18
|
+
Please read [`[5.0.0]`](CHANGELOG.md#500---2021-11-05) section in [CHANGELOG](CHANGELOG.md)
|
14
19
|
|
15
20
|
# Related library
|
16
21
|
- https://github.com/appium/ruby_lib
|
@@ -38,7 +43,7 @@ $ appium --relaxed-security # To run all tests in local
|
|
38
43
|
```bash
|
39
44
|
$ bundle install
|
40
45
|
$ rake test:func:android # Andorid, uiautomator2
|
41
|
-
$
|
46
|
+
$ APPIUM_DRIVER=espresso rake test:func:android # Andorid, uiautomator2
|
42
47
|
$ rake test:func:ios # iOS
|
43
48
|
```
|
44
49
|
|
@@ -78,7 +83,7 @@ $ PARALLEL=1 bundle exec parallel_test test/functional/ios -n 2
|
|
78
83
|
|
79
84
|
- Runs on CI environment (on Azure)
|
80
85
|
- Non `IGNORE_VERSION_SKIP` or `IGNORE_VERSION_SKIP=true` runs all tests ignoring `skip` them by Appium versions
|
81
|
-
- `IGNORE_VERSION_SKIP=false` skips the following tests if the Appium version is lower than the requirement
|
86
|
+
- `IGNORE_VERSION_SKIP=false` skips the following tests if the Appium version is lower than the requirement
|
82
87
|
|
83
88
|
```
|
84
89
|
$ IGNORE_VERSION_SKIP=true CI=true bundle exec rake test:func:android
|
@@ -92,9 +97,9 @@ $ IGNORE_VERSION_SKIP=true CI=true bundle exec rake test:func:android
|
|
92
97
|
```ruby
|
93
98
|
require 'rubygems'
|
94
99
|
require 'appium_lib_core'
|
95
|
-
|
100
|
+
|
96
101
|
opts = {
|
97
|
-
|
102
|
+
capabilities: { # or { caps: {....} }
|
98
103
|
platformName: :ios,
|
99
104
|
platformVersion: '11.0',
|
100
105
|
deviceName: 'iPhone Simulator',
|
@@ -107,7 +112,7 @@ $ IGNORE_VERSION_SKIP=true CI=true bundle exec rake test:func:android
|
|
107
112
|
}
|
108
113
|
@core = Appium::Core.for(opts) # create a core driver with `opts`
|
109
114
|
@driver = @core.start_driver
|
110
|
-
|
115
|
+
|
111
116
|
# Launch iPhone Simulator and `MyiOS.app`
|
112
117
|
@driver.find_element(:accessibility_id, 'some accessibility') # find an element
|
113
118
|
```
|
@@ -115,12 +120,12 @@ $ IGNORE_VERSION_SKIP=true CI=true bundle exec rake test:func:android
|
|
115
120
|
```bash
|
116
121
|
# shell 1
|
117
122
|
$ appium --log-level warn:error # show only warning and error logs
|
118
|
-
|
123
|
+
|
119
124
|
# shell 2
|
120
125
|
$ ruby test.rb
|
121
126
|
```
|
122
127
|
|
123
|
-
More examples are in [test/functional](test/functional)
|
128
|
+
More examples are in [test/functional](test/functional)
|
124
129
|
|
125
130
|
### Capabilities
|
126
131
|
|
data/Rakefile
CHANGED
@@ -38,6 +38,7 @@ namespace :test do
|
|
38
38
|
namespace :unit do
|
39
39
|
desc('Run all iOS related unit tests in test directory')
|
40
40
|
Rake::TestTask.new(:ios) do |t|
|
41
|
+
ENV['UNIT_TEST'] = '1'
|
41
42
|
t.libs << 'test'
|
42
43
|
t.libs << 'lib'
|
43
44
|
t.test_files = FileList['test/unit/ios/**/*_test.rb']
|
@@ -45,6 +46,7 @@ namespace :test do
|
|
45
46
|
|
46
47
|
desc('Run all Android related unit tests in test directory')
|
47
48
|
Rake::TestTask.new(:android) do |t|
|
49
|
+
ENV['UNIT_TEST'] = '1'
|
48
50
|
t.libs << 'test'
|
49
51
|
t.libs << 'lib'
|
50
52
|
t.test_files = FileList['test/unit/android/**/*_test.rb']
|
@@ -52,6 +54,7 @@ namespace :test do
|
|
52
54
|
|
53
55
|
desc('Run all common related unit tests in test directory')
|
54
56
|
Rake::TestTask.new(:common) do |t|
|
57
|
+
ENV['UNIT_TEST'] = '1'
|
55
58
|
t.libs << 'test'
|
56
59
|
t.libs << 'lib'
|
57
60
|
t.test_files = FileList['test/unit/common/**/*_test.rb']
|
@@ -59,6 +62,7 @@ namespace :test do
|
|
59
62
|
|
60
63
|
desc('Run all Windows related unit tests in test directory')
|
61
64
|
Rake::TestTask.new(:windows) do |t|
|
65
|
+
ENV['UNIT_TEST'] = '1'
|
62
66
|
t.libs << 'test'
|
63
67
|
t.libs << 'lib'
|
64
68
|
t.test_files = FileList['test/unit/windows/**/*_test.rb']
|
data/appium_lib_core.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'appium_lib_core/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.required_ruby_version = '>= 2.
|
7
|
+
spec.required_ruby_version = '>= 2.7'
|
8
8
|
|
9
9
|
spec.name = 'appium_lib_core'
|
10
10
|
spec.version = Appium::Core::VERSION
|
@@ -16,25 +16,22 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.homepage = 'https://github.com/appium/ruby_lib_core/'
|
17
17
|
spec.license = 'Apache-2.0'
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
-
f.match(%r{^(doc|test|spec|features)
|
19
|
+
f.match(%r{(^(doc|docs|test|spec|features|ci-jobs|.github|script)/|release_notes.md|.gitignore|azure-pipelines.yml|.rubocop.yml)})
|
20
20
|
end
|
21
21
|
spec.bindir = 'exe'
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
|
-
spec.add_runtime_dependency 'selenium-webdriver', '~>
|
25
|
+
spec.add_runtime_dependency 'selenium-webdriver', '~> 4.2', '< 4.6'
|
26
26
|
spec.add_runtime_dependency 'faye-websocket', '~> 0.11.0'
|
27
27
|
|
28
|
-
spec.add_development_dependency 'bundler', '>= 1.14'
|
29
28
|
spec.add_development_dependency 'rake', '~> 13.0'
|
30
29
|
spec.add_development_dependency 'yard', '~> 0.9.11'
|
31
30
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
32
31
|
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
33
|
-
spec.add_development_dependency 'webmock', '~> 3.
|
34
|
-
spec.add_development_dependency 'rubocop', '1.
|
32
|
+
spec.add_development_dependency 'webmock', '~> 3.18.1'
|
33
|
+
spec.add_development_dependency 'rubocop', '1.36.0'
|
35
34
|
spec.add_development_dependency 'appium_thor', '~> 1.0'
|
36
|
-
spec.add_development_dependency 'pry'
|
37
|
-
spec.add_development_dependency 'pry-byebug'
|
38
35
|
spec.add_development_dependency 'parallel_tests'
|
39
36
|
spec.add_development_dependency 'simplecov'
|
40
37
|
end
|
data/bin/console
CHANGED
@@ -6,9 +6,5 @@ require "appium_lib_core"
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
8
8
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
9
|
require "irb"
|
14
10
|
IRB.start(__FILE__)
|
@@ -21,7 +21,8 @@ module Appium
|
|
21
21
|
::Appium::Core::Device.add_endpoint_method(:finger_print) do
|
22
22
|
def finger_print(finger_id)
|
23
23
|
unless (1..10).cover? finger_id.to_i
|
24
|
-
raise ArgumentError,
|
24
|
+
raise ::Appium::Core::Error::ArgumentError,
|
25
|
+
"finger_id should be integer between 1 to 10. Not #{finger_id}"
|
25
26
|
end
|
26
27
|
|
27
28
|
execute(:finger_print, {}, { fingerprintId: finger_id.to_i })
|
@@ -290,16 +290,16 @@ module Appium
|
|
290
290
|
# @driver.finger_print 1
|
291
291
|
#
|
292
292
|
|
293
|
-
# @!method execute_cdp(cmd, params)
|
293
|
+
# @!method execute_cdp(cmd, **params)
|
294
294
|
# Execute Chrome Devtools protocol commands
|
295
295
|
# https://chromedevtools.github.io/devtools-protocol
|
296
296
|
#
|
297
297
|
# @param [String] cmd The name of command
|
298
|
-
# @
|
298
|
+
# @option params The parameter for the command as keyword options.
|
299
299
|
#
|
300
300
|
# @example
|
301
301
|
#
|
302
|
-
# @driver.execute_cdp 'Page.captureScreenshot',
|
302
|
+
# @driver.execute_cdp 'Page.captureScreenshot', quality: 50, format: 'jpeg'
|
303
303
|
# @driver.execute_cdp 'Page.getResourceTree'
|
304
304
|
#
|
305
305
|
|
@@ -415,7 +415,7 @@ module Appium
|
|
415
415
|
# SeleniumWebdriver could already define this method
|
416
416
|
return if method_defined? :execute_cdp
|
417
417
|
|
418
|
-
def execute_cdp(cmd, params
|
418
|
+
def execute_cdp(cmd, **params)
|
419
419
|
execute :chrome_send_command, {}, { cmd: cmd, params: params }
|
420
420
|
end
|
421
421
|
end
|
@@ -16,52 +16,45 @@ module Appium
|
|
16
16
|
module Core
|
17
17
|
class Base
|
18
18
|
class Bridge < ::Selenium::WebDriver::Remote::Bridge
|
19
|
+
include Device::DeviceLock
|
20
|
+
include Device::Keyboard
|
21
|
+
include Device::ImeActions
|
22
|
+
include Device::Setting
|
23
|
+
include Device::Context
|
24
|
+
include Device::Value
|
25
|
+
include Device::FileManagement
|
26
|
+
include Device::KeyEvent
|
27
|
+
include Device::ImageComparison
|
28
|
+
include Device::AppManagement
|
29
|
+
include Device::AppState
|
30
|
+
include Device::ScreenRecord::Command
|
31
|
+
include Device::Device
|
32
|
+
include Device::TouchActions
|
33
|
+
include Device::ExecuteDriver
|
34
|
+
include Device::Orientation
|
35
|
+
|
19
36
|
# Prefix for extra capability defined by W3C
|
20
37
|
APPIUM_PREFIX = 'appium:'
|
21
38
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
# Almost same as self.handshake in ::Selenium::WebDriver::Remote::Bridge
|
26
|
-
#
|
27
|
-
# Implements protocol handshake which:
|
28
|
-
#
|
29
|
-
# 1. Creates session with driver.
|
30
|
-
# 2. Sniffs response.
|
31
|
-
# 3. Based on the response, understands which dialect we should use.
|
32
|
-
#
|
33
|
-
# @return [Bridge::MJSONWP, Bridge::W3C]
|
34
|
-
#
|
35
|
-
def self.handshake(**opts)
|
36
|
-
desired_capabilities = opts.delete(:desired_capabilities) { ::Selenium::WebDriver::Remote::Capabilities.new }
|
39
|
+
# No 'browserName' means the session is native appium connection
|
40
|
+
APPIUM_NATIVE_BROWSER_NAME = 'appium'
|
37
41
|
|
38
|
-
|
39
|
-
unless ::Selenium::WebDriver::Remote::Capabilities.respond_to?(desired_capabilities)
|
40
|
-
raise ::Selenium::WebDriver::Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
|
41
|
-
end
|
42
|
+
attr_reader :available_commands
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
case bridge.dialect
|
50
|
-
when :oss # for MJSONWP
|
51
|
-
Bridge::MJSONWP.new(capabilities, bridge.session_id, **opts)
|
52
|
-
when :w3c
|
53
|
-
Bridge::W3C.new(capabilities, bridge.session_id, **opts)
|
54
|
-
else
|
55
|
-
raise CoreError, 'cannot understand dialect'
|
44
|
+
def browser
|
45
|
+
@browser ||= begin
|
46
|
+
name = @capabilities.browser_name
|
47
|
+
name ? name.tr(' ', '_').downcase.to_sym : 'unknown'
|
48
|
+
rescue KeyError
|
49
|
+
APPIUM_NATIVE_BROWSER_NAME
|
56
50
|
end
|
57
51
|
end
|
58
52
|
|
59
53
|
# Override
|
60
|
-
# Creates session handling
|
61
|
-
# Copy from Selenium::WebDriver::Remote::Bridge to keep using +merged_capabilities+ for Appium
|
54
|
+
# Creates session handling.
|
62
55
|
#
|
63
|
-
# @param [::
|
64
|
-
# @return [::
|
56
|
+
# @param [::Appium::Core::Base::Capabilities, Hash] capabilities A capability
|
57
|
+
# @return [::Appium::Core::Base::Capabilities]
|
65
58
|
#
|
66
59
|
# @example
|
67
60
|
#
|
@@ -79,39 +72,29 @@ module Appium
|
|
79
72
|
# }
|
80
73
|
# }
|
81
74
|
# core = ::Appium::Core.for(caps)
|
82
|
-
# driver = core.start_driver
|
75
|
+
# driver = core.start_driver
|
83
76
|
#
|
84
|
-
def create_session(
|
85
|
-
|
86
|
-
|
87
|
-
@session_id = response['sessionId']
|
88
|
-
oss_status = response['status'] # for compatibility with Appium 1.7.1-
|
89
|
-
value = response['value']
|
90
|
-
|
91
|
-
if value.is_a?(Hash) # include for W3C format
|
92
|
-
@session_id = value['sessionId'] if value.key?('sessionId')
|
77
|
+
def create_session(capabilities)
|
78
|
+
@available_commands = ::Appium::Core::Commands::COMMANDS.dup
|
93
79
|
|
94
|
-
|
95
|
-
|
96
|
-
elsif value.key?('value')
|
97
|
-
value = value['value']
|
98
|
-
end
|
99
|
-
end
|
80
|
+
always_match = add_appium_prefix(capabilities)
|
81
|
+
response = execute(:new_session, {}, { capabilities: { alwaysMatch: always_match, firstMatch: [{}] } })
|
100
82
|
|
83
|
+
@session_id = response['sessionId']
|
101
84
|
raise ::Selenium::WebDriver::Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
|
102
85
|
|
103
|
-
json_create(
|
86
|
+
@capabilities = json_create(response['capabilities'])
|
104
87
|
end
|
105
88
|
|
106
89
|
# Append +appium:+ prefix for Appium following W3C spec
|
107
90
|
# https://www.w3.org/TR/webdriver/#dfn-validate-capabilities
|
108
91
|
#
|
109
|
-
# @param [::
|
110
|
-
# @return [::
|
92
|
+
# @param [::Appium::Core::Base::Capabilities, Hash] capabilities A capability
|
93
|
+
# @return [::Appium::Core::Base::Capabilities]
|
111
94
|
def add_appium_prefix(capabilities)
|
112
|
-
w3c_capabilities = ::
|
95
|
+
w3c_capabilities = ::Appium::Core::Base::Capabilities.new
|
113
96
|
|
114
|
-
capabilities = capabilities.
|
97
|
+
capabilities = capabilities.send(:capabilities) unless capabilities.is_a?(Hash)
|
115
98
|
|
116
99
|
capabilities.each do |name, value|
|
117
100
|
next if value.nil?
|
@@ -128,61 +111,277 @@ module Appium
|
|
128
111
|
|
129
112
|
private
|
130
113
|
|
131
|
-
def camel_case(
|
132
|
-
|
114
|
+
def camel_case(str_or_sym)
|
115
|
+
str_or_sym.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
|
133
116
|
end
|
134
117
|
|
135
118
|
def extension_prefix?(capability_name)
|
136
|
-
snake_cased_capability_names = ::
|
119
|
+
snake_cased_capability_names = ::Appium::Core::Base::Capabilities::KNOWN.map(&:to_s)
|
137
120
|
camel_cased_capability_names = snake_cased_capability_names.map { |v| camel_case(v) }
|
138
121
|
|
122
|
+
# Check 'EXTENSION_CAPABILITY_PATTERN'
|
139
123
|
snake_cased_capability_names.include?(capability_name) ||
|
140
124
|
camel_cased_capability_names.include?(capability_name) ||
|
141
|
-
capability_name.match(
|
125
|
+
capability_name.match(':')
|
142
126
|
end
|
143
127
|
|
144
|
-
def json_create(
|
145
|
-
|
146
|
-
::Selenium::WebDriver.logger.info 'Detected OSS dialect.'
|
147
|
-
@dialect = :oss
|
148
|
-
::Selenium::WebDriver::Remote::Capabilities.json_create(value)
|
149
|
-
else
|
150
|
-
::Selenium::WebDriver.logger.info 'Detected W3C dialect.'
|
151
|
-
@dialect = :w3c
|
152
|
-
::Selenium::WebDriver::Remote::W3C::Capabilities.json_create(value)
|
153
|
-
end
|
128
|
+
def json_create(value)
|
129
|
+
::Appium::Core::Base::Capabilities.json_create(value)
|
154
130
|
end
|
155
131
|
|
156
|
-
|
157
|
-
w3c_capabilities = ::Selenium::WebDriver::Remote::W3C::Capabilities.new
|
132
|
+
public
|
158
133
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
next if value.is_a?(String) && value.empty?
|
163
|
-
next if name == FORCE_MJSONWP
|
134
|
+
# command for Appium 2.0.
|
135
|
+
def add_command(method:, url:, name:, &block)
|
136
|
+
::Appium::Logger.info "Overriding the method '#{name}' for '#{url}'" if @available_commands.key? name
|
164
137
|
|
165
|
-
|
138
|
+
@available_commands[name] = [method, url]
|
139
|
+
|
140
|
+
::Appium::Core::Device.add_endpoint_method name, &block
|
141
|
+
end
|
142
|
+
|
143
|
+
def commands(command)
|
144
|
+
@available_commands[command]
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns all available sessions on the Appium server instance
|
148
|
+
def sessions
|
149
|
+
execute :get_all_sessions
|
150
|
+
end
|
151
|
+
|
152
|
+
def status
|
153
|
+
execute :status
|
154
|
+
end
|
155
|
+
|
156
|
+
# Perform 'touch' actions for W3C module.
|
157
|
+
# Generate +touch+ pointer action here and users can use this via +driver.action+
|
158
|
+
# - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/W3CActionBuilder.html
|
159
|
+
# - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/PointerActions.html
|
160
|
+
# - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/KeyActions.html
|
161
|
+
#
|
162
|
+
# The pointer type is 'touch' by default in the Appium Ruby client.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
#
|
166
|
+
# element = @driver.find_element(:id, "some id")
|
167
|
+
# @driver.action.click(element).perform # The 'click' is a part of 'PointerActions'
|
168
|
+
#
|
169
|
+
def action(_deprecated_async = nil, async: false, devices: nil)
|
170
|
+
::Selenium::WebDriver::ActionBuilder.new(
|
171
|
+
self,
|
172
|
+
devices: devices || [::Selenium::WebDriver::Interactions.pointer(:touch, name: 'touch')],
|
173
|
+
async: async,
|
174
|
+
duration: 50 # milliseconds
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Port from MJSONWP
|
179
|
+
def get_timeouts
|
180
|
+
execute :get_timeouts
|
181
|
+
end
|
182
|
+
|
183
|
+
# Port from MJSONWP
|
184
|
+
def session_capabilities
|
185
|
+
::Appium::Core::Base::Capabilities.json_create execute(:get_capabilities)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Override for safe. Newer ruby selenium webdriver already has the same code
|
189
|
+
def page_source
|
190
|
+
execute :get_page_source
|
191
|
+
end
|
192
|
+
|
193
|
+
# For Appium
|
194
|
+
# override
|
195
|
+
def element_displayed?(element)
|
196
|
+
# For W3C
|
197
|
+
# https://github.com/SeleniumHQ/selenium/commit/b618499adcc3a9f667590652c5757c0caa703289
|
198
|
+
# execute_atom :isDisplayed, element
|
199
|
+
execute :is_element_displayed, id: element.id
|
200
|
+
end
|
201
|
+
|
202
|
+
# For Appium
|
203
|
+
# override
|
204
|
+
def element_attribute(element, name)
|
205
|
+
# For W3C in Selenium Client
|
206
|
+
# execute_atom :getAttribute, element, name
|
207
|
+
execute :get_element_attribute, id: element.id, name: name
|
208
|
+
end
|
209
|
+
|
210
|
+
# For Appium
|
211
|
+
# override
|
212
|
+
def active_element
|
213
|
+
::Appium::Core::Element.new self, element_id_from(execute(:get_active_element))
|
214
|
+
end
|
215
|
+
alias switch_to_active_element active_element
|
216
|
+
|
217
|
+
# For Appium
|
218
|
+
# override
|
219
|
+
def find_element_by(how, what, parent_ref = [])
|
220
|
+
how, what = convert_locator(how, what)
|
221
|
+
|
222
|
+
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
|
223
|
+
|
224
|
+
parent_type, parent_id = parent_ref
|
225
|
+
id = case parent_type
|
226
|
+
when :element
|
227
|
+
execute :find_child_element, { id: parent_id }, { using: how, value: what.to_s }
|
228
|
+
when :shadow_root
|
229
|
+
execute :find_shadow_child_element, { id: parent_id }, { using: how, value: what.to_s }
|
230
|
+
else
|
231
|
+
execute :find_element, {}, { using: how, value: what.to_s }
|
232
|
+
end
|
233
|
+
|
234
|
+
::Appium::Core::Element.new self, element_id_from(id)
|
235
|
+
end
|
236
|
+
|
237
|
+
# For Appium
|
238
|
+
# override
|
239
|
+
def find_elements_by(how, what, parent_ref = [])
|
240
|
+
how, what = convert_locator(how, what)
|
241
|
+
|
242
|
+
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
|
243
|
+
|
244
|
+
parent_type, parent_id = parent_ref
|
245
|
+
ids = case parent_type
|
246
|
+
when :element
|
247
|
+
execute :find_child_elements, { id: parent_id }, { using: how, value: what.to_s }
|
248
|
+
when :shadow_root
|
249
|
+
execute :find_shadow_child_elements, { id: parent_id }, { using: how, value: what.to_s }
|
250
|
+
else
|
251
|
+
execute :find_elements, {}, { using: how, value: what.to_s }
|
252
|
+
end
|
253
|
+
|
254
|
+
ids.map { |id| ::Appium::Core::Element.new self, element_id_from(id) }
|
255
|
+
end
|
256
|
+
|
257
|
+
# For Appium
|
258
|
+
# @param [Hash] id The id which can get as a response from server
|
259
|
+
# @return [::Appium::Core::Element]
|
260
|
+
def convert_to_element(id)
|
261
|
+
::Appium::Core::Element.new self, element_id_from(id)
|
262
|
+
end
|
263
|
+
|
264
|
+
# For Appium
|
265
|
+
# override
|
266
|
+
# called in 'extend DriverExtensions::HasNetworkConnection'
|
267
|
+
def network_connection
|
268
|
+
execute :get_network_connection
|
269
|
+
end
|
270
|
+
|
271
|
+
# For Appium
|
272
|
+
# override
|
273
|
+
# called in 'extend DriverExtensions::HasNetworkConnection'
|
274
|
+
def network_connection=(type)
|
275
|
+
execute :set_network_connection, {}, { parameters: { type: type } }
|
276
|
+
end
|
277
|
+
|
278
|
+
# For Appium
|
279
|
+
# No implementation for W3C webdriver module
|
280
|
+
# called in 'extend DriverExtensions::HasLocation'
|
281
|
+
def location
|
282
|
+
obj = execute(:get_location) || {}
|
283
|
+
::Selenium::WebDriver::Location.new obj['latitude'], obj['longitude'], obj['altitude']
|
284
|
+
end
|
285
|
+
|
286
|
+
# For Appium
|
287
|
+
# No implementation for W3C webdriver module
|
288
|
+
def set_location(lat, lon, alt = 0.0, speed: nil, satellites: nil)
|
289
|
+
loc = { latitude: lat, longitude: lon, altitude: alt }
|
290
|
+
loc[:speed] = speed unless speed.nil?
|
291
|
+
loc[:satellites] = satellites unless satellites.nil?
|
292
|
+
execute :set_location, {}, { location: loc }
|
293
|
+
end
|
294
|
+
|
295
|
+
#
|
296
|
+
# logs
|
297
|
+
#
|
298
|
+
# For Appium
|
299
|
+
# No implementation for W3C webdriver module
|
300
|
+
def available_log_types
|
301
|
+
types = execute :get_available_log_types
|
302
|
+
Array(types).map(&:to_sym)
|
303
|
+
end
|
304
|
+
|
305
|
+
# For Appium
|
306
|
+
# No implementation for W3C webdriver module
|
307
|
+
def log(type)
|
308
|
+
data = execute :get_log, {}, { type: type.to_s }
|
309
|
+
|
310
|
+
Array(data).map do |l|
|
311
|
+
::Selenium::WebDriver::LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
|
312
|
+
rescue KeyError
|
313
|
+
next
|
166
314
|
end
|
315
|
+
end
|
167
316
|
|
168
|
-
|
317
|
+
# For Appium
|
318
|
+
def log_event(vendor, event)
|
319
|
+
execute :post_log_event, {}, { vendor: vendor, event: event }
|
169
320
|
end
|
170
321
|
|
171
|
-
|
172
|
-
|
173
|
-
|
322
|
+
# For Appium
|
323
|
+
def log_events(type = nil)
|
324
|
+
args = {}
|
325
|
+
args['type'] = type unless type.nil?
|
174
326
|
|
175
|
-
|
327
|
+
execute :get_log_events, {}, args
|
328
|
+
end
|
176
329
|
|
177
|
-
|
178
|
-
|
330
|
+
def viewport_screenshot
|
331
|
+
execute_script('mobile: viewportScreenshot')
|
332
|
+
end
|
333
|
+
|
334
|
+
def element_screenshot(element_id)
|
335
|
+
execute :take_element_screenshot, id: element_id
|
336
|
+
end
|
337
|
+
|
338
|
+
private
|
339
|
+
|
340
|
+
def unwrap_script_result(arg)
|
341
|
+
case arg
|
342
|
+
when Array
|
343
|
+
arg.map { |e| unwrap_script_result(e) }
|
344
|
+
when Hash
|
345
|
+
element_id = element_id_from(arg)
|
346
|
+
return ::Appium::Core::Element.new(self, element_id) if element_id
|
347
|
+
|
348
|
+
shadow_root_id = shadow_root_id_from(arg)
|
349
|
+
return ::Selenium::WebDriver::Remote::ShadowRoot.new self, shadow_root_id if shadow_root_id
|
350
|
+
|
351
|
+
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
|
352
|
+
else
|
353
|
+
arg
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def element_id_from(id)
|
358
|
+
id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
|
359
|
+
end
|
179
360
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
}
|
361
|
+
# Don't convert locators for Appium in native context
|
362
|
+
def convert_locator(how, what)
|
363
|
+
# case how
|
364
|
+
# when 'class name'
|
365
|
+
# how = 'css selector'
|
366
|
+
# what = ".#{escape_css(what)}"
|
367
|
+
# when 'id'
|
368
|
+
# how = 'css selector'
|
369
|
+
# what = "##{escape_css(what)}"
|
370
|
+
# when 'name'
|
371
|
+
# how = 'css selector'
|
372
|
+
# what = "*[name='#{escape_css(what)}']"
|
373
|
+
# when 'tag name'
|
374
|
+
# how = 'css selector'
|
375
|
+
# end
|
376
|
+
#
|
377
|
+
# if what.is_a?(Hash)
|
378
|
+
# what = what.each_with_object({}) do |(h, w), hash|
|
379
|
+
# h, w = convert_locator(h.to_s, w)
|
380
|
+
# hash[h] = w
|
381
|
+
# end
|
382
|
+
# end
|
383
|
+
|
384
|
+
[how, what]
|
186
385
|
end
|
187
386
|
end # class Bridge
|
188
387
|
end # class Base
|
@@ -15,15 +15,14 @@
|
|
15
15
|
module Appium
|
16
16
|
module Core
|
17
17
|
class Base
|
18
|
-
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
end
|
18
|
+
class Capabilities < ::Selenium::WebDriver::Remote::Capabilities
|
19
|
+
# TODO: Move to 'Options' way instead of 'Capabilities'.
|
20
|
+
# Selenium 5 will have Options instead of 'Capabilities'.
|
21
|
+
# https://github.com/SeleniumHQ/selenium/blob/trunk/rb/lib/selenium/webdriver/common/options.rb
|
22
|
+
# Then, Ruby client also shoud move to the Options way.
|
23
|
+
# Appium's capabilities could change by depending on Appium versions. So it does not have
|
24
|
+
# standard options like chrome and firefox etc. So, the implementation should differ from
|
25
|
+
# other browsers. But here should inherit `Options` to follow Selenium.
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|