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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +158 -275
  3. data/README.md +13 -8
  4. data/Rakefile +4 -0
  5. data/appium_lib_core.gemspec +5 -8
  6. data/bin/console +0 -4
  7. data/lib/appium_lib_core/android/device/auth_finger_print.rb +2 -1
  8. data/lib/appium_lib_core/android/device.rb +4 -4
  9. data/lib/appium_lib_core/common/base/bridge.rb +291 -92
  10. data/lib/appium_lib_core/common/base/capabilities.rb +8 -9
  11. data/lib/appium_lib_core/common/base/device_ime.rb +49 -0
  12. data/lib/appium_lib_core/common/base/driver.rb +225 -187
  13. data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
  14. data/lib/appium_lib_core/common/base/has_location.rb +80 -0
  15. data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
  16. data/lib/appium_lib_core/common/base/http_default.rb +1 -3
  17. data/lib/appium_lib_core/common/base/remote_status.rb +31 -0
  18. data/lib/appium_lib_core/common/base/rotable.rb +54 -0
  19. data/lib/appium_lib_core/common/base/screenshot.rb +6 -6
  20. data/lib/appium_lib_core/common/base/search_context.rb +20 -5
  21. data/lib/appium_lib_core/common/base.rb +1 -3
  22. data/lib/appium_lib_core/common/command.rb +255 -4
  23. data/lib/appium_lib_core/common/device/app_management.rb +2 -14
  24. data/lib/appium_lib_core/common/device/image_comparison.rb +12 -4
  25. data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
  26. data/lib/appium_lib_core/common/{command/mjsonwp.rb → device/orientation.rb} +14 -11
  27. data/lib/appium_lib_core/common/device/touch_actions.rb +2 -0
  28. data/lib/appium_lib_core/common/device/value.rb +6 -6
  29. data/lib/appium_lib_core/common/error.rb +4 -1
  30. data/lib/appium_lib_core/common/log.rb +4 -1
  31. data/lib/appium_lib_core/common/touch_action/multi_touch.rb +19 -0
  32. data/lib/appium_lib_core/common/touch_action/touch_actions.rb +16 -2
  33. data/lib/appium_lib_core/common/wait.rb +38 -6
  34. data/lib/appium_lib_core/device.rb +1 -5
  35. data/lib/appium_lib_core/driver.rb +41 -75
  36. data/lib/appium_lib_core/{patch.rb → element.rb} +66 -9
  37. data/lib/appium_lib_core/ios/uiautomation/patch.rb +1 -1
  38. data/lib/appium_lib_core/{common/base/command.rb → mac2/bridge.rb} +9 -8
  39. data/lib/appium_lib_core/mac2/device/screen.rb +48 -0
  40. data/lib/appium_lib_core/mac2/device.rb +92 -0
  41. data/lib/appium_lib_core/mac2.rb +17 -0
  42. data/lib/appium_lib_core/version.rb +2 -2
  43. data/lib/appium_lib_core.rb +2 -5
  44. metadata +29 -82
  45. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  46. data/.github/contributing.md +0 -26
  47. data/.github/issue_template.md +0 -20
  48. data/.github/workflows/unittest.yml +0 -68
  49. data/.gitignore +0 -18
  50. data/.rubocop.yml +0 -58
  51. data/azure-pipelines.yml +0 -15
  52. data/ci-jobs/functional/android_setup.yml +0 -3
  53. data/ci-jobs/functional/ios_setup.yml +0 -7
  54. data/ci-jobs/functional/publish_test_result.yml +0 -18
  55. data/ci-jobs/functional/run_appium.yml +0 -25
  56. data/ci-jobs/functional/start-emulator.sh +0 -26
  57. data/ci-jobs/functional_test.yml +0 -298
  58. data/docs/mobile_command.md +0 -34
  59. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -81
  60. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -252
  61. data/lib/appium_lib_core/common/command/common.rb +0 -110
  62. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
  63. data/release_notes.md +0 -816
  64. 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
- - http://www.rubydoc.info/github/appium/ruby_lib_core
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
- $ AUTOMATION_NAME_DROID=espresso rake test:func:android # Andorid, uiautomator2
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
- desired_capabilities: { # or { caps: {....} }
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']
@@ -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.4'
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', '~> 3.14', '>= 3.14.1'
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.11.0'
34
- spec.add_development_dependency 'rubocop', '1.7.0'
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, "finger_id should be integer between 1 to 10. Not #{finger_id}"
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
- # @param [Hash] params The parameter for the command as hash.
298
+ # @option params The parameter for the command as keyword options.
299
299
  #
300
300
  # @example
301
301
  #
302
- # @driver.execute_cdp 'Page.captureScreenshot', { quality: 50, format: 'jpeg' }
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
- # TODO: Remove the forceMjsonwp after Appium server won't need it
23
- FORCE_MJSONWP = :forceMjsonwp
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
- if desired_capabilities.is_a?(Symbol)
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
- desired_capabilities = ::Selenium::WebDriver::Remote::Capabilities.__send__(desired_capabilities)
44
- end
45
-
46
- bridge = new(opts)
47
- capabilities = bridge.create_session(desired_capabilities)
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 both OSS and W3C dialects.
61
- # Copy from Selenium::WebDriver::Remote::Bridge to keep using +merged_capabilities+ for Appium
54
+ # Creates session handling.
62
55
  #
63
- # @param [::Selenium::WebDriver::Remote::W3C::Capabilities, Hash] desired_capabilities A capability
64
- # @return [::Selenium::WebDriver::Remote::Capabilities, ::Selenium::WebDriver::Remote::W3C::Capabilities]
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 #=> driver.dialect == :w3c if the Appium server support W3C.
75
+ # driver = core.start_driver
83
76
  #
84
- def create_session(desired_capabilities)
85
- response = execute(:new_session, {}, merged_capabilities(desired_capabilities))
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
- if value.key?('capabilities')
95
- value = value['capabilities']
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(oss_status, value)
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 [::Selenium::WebDriver::Remote::W3C::Capabilities, Hash] capabilities A capability
110
- # @return [::Selenium::WebDriver::Remote::W3C::Capabilities]
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 = ::Selenium::WebDriver::Remote::W3C::Capabilities.new
95
+ w3c_capabilities = ::Appium::Core::Base::Capabilities.new
113
96
 
114
- capabilities = capabilities.__send__(:capabilities) unless capabilities.is_a?(Hash)
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(str)
132
- str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
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 = ::Selenium::WebDriver::Remote::W3C::Capabilities::KNOWN.map(&:to_s)
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(::Selenium::WebDriver::Remote::W3C::Capabilities::EXTENSION_CAPABILITY_PATTERN)
125
+ capability_name.match(':')
142
126
  end
143
127
 
144
- def json_create(oss_status, value)
145
- if oss_status
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
- def delete_force_mjsonwp(capabilities)
157
- w3c_capabilities = ::Selenium::WebDriver::Remote::W3C::Capabilities.new
132
+ public
158
133
 
159
- capabilities = capabilities.__send__(:capabilities) unless capabilities.is_a?(Hash)
160
- capabilities.each do |name, value|
161
- next if value.nil?
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
- w3c_capabilities[name] = value
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
- w3c_capabilities
317
+ # For Appium
318
+ def log_event(vendor, event)
319
+ execute :post_log_event, {}, { vendor: vendor, event: event }
169
320
  end
170
321
 
171
- def merged_capabilities(desired_capabilities)
172
- force_mjsonwp = desired_capabilities[FORCE_MJSONWP]
173
- desired_capabilities = delete_force_mjsonwp(desired_capabilities) unless force_mjsonwp.nil?
322
+ # For Appium
323
+ def log_events(type = nil)
324
+ args = {}
325
+ args['type'] = type unless type.nil?
174
326
 
175
- ::Appium::Logger.warn "'forceMjsonwp' no longer works. Sending both W3C and MJSONWP capabilities" if force_mjsonwp
327
+ execute :get_log_events, {}, args
328
+ end
176
329
 
177
- new_caps = add_appium_prefix(desired_capabilities)
178
- w3c_capabilities = ::Selenium::WebDriver::Remote::W3C::Capabilities.from_oss(new_caps)
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
- desiredCapabilities: desired_capabilities,
182
- capabilities: {
183
- firstMatch: [w3c_capabilities]
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
- module Capabilities
19
- # @private
20
- # @param [Hash] opts_caps Capabilities for Appium server. All capability keys are converted to lowerCamelCase when
21
- # this client sends capabilities to Appium server as JSON format.
22
- # @return [::Selenium::WebDriver::Remote::W3C::Capabilities] Return instance of Appium::Core::Base::Capabilities
23
- # inherited ::Selenium::WebDriver::Remote::W3C::Capabilities
24
- def self.create_capabilities(opts_caps = {})
25
- ::Selenium::WebDriver::Remote::W3C::Capabilities.new(opts_caps)
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