appium_lib_core 4.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +129 -275
  3. data/README.md +12 -7
  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 -90
  10. data/lib/appium_lib_core/common/base/capabilities.rb +10 -3
  11. data/lib/appium_lib_core/common/base/device_ime.rb +49 -0
  12. data/lib/appium_lib_core/common/base/driver.rb +223 -184
  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 +19 -4
  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 +42 -50
  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 +25 -84
  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
@@ -11,6 +11,11 @@ This library wraps [selenium-webdriver](https://github.com/SeleniumHQ/selenium/w
11
11
  # Documentation
12
12
 
13
13
  - http://www.rubydoc.info/github/appium/ruby_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'
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.14.0'
33
+ spec.add_development_dependency 'rubocop', '1.30.1'
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 [::Selenium::WebDriver::Remote::Capabilities, Hash] capabilities A capability
57
+ # @return [::Selenium::WebDriver::Remote::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))
77
+ def create_session(capabilities)
78
+ @available_commands = ::Appium::Core::Commands::COMMANDS.dup
86
79
 
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')
93
-
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 [::Selenium::WebDriver::Remote::Capabilities, Hash] capabilities A capability
93
+ # @return [::Selenium::WebDriver::Remote::Capabilities]
111
94
  def add_appium_prefix(capabilities)
112
- w3c_capabilities = ::Selenium::WebDriver::Remote::W3C::Capabilities.new
95
+ w3c_capabilities = ::Selenium::WebDriver::Remote::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?
@@ -133,56 +116,274 @@ module Appium
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 = ::Selenium::WebDriver::Remote::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
+ ::Selenium::WebDriver::Remote::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
137
+
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
+ ::Selenium::WebDriver::Remote::Capabilities.json_create execute(:get_capabilities)
186
+ end
187
+
188
+ # For Appium
189
+ # override
190
+ def page_source
191
+ # For W3C
192
+ # execute_script('var source = document.documentElement.outerHTML;' \
193
+ # 'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
194
+ # 'return source;')
195
+ execute :get_page_source
196
+ end
164
197
 
165
- w3c_capabilities[name] = value
198
+ # For Appium
199
+ # override
200
+ def element_displayed?(element)
201
+ # For W3C
202
+ # https://github.com/SeleniumHQ/selenium/commit/b618499adcc3a9f667590652c5757c0caa703289
203
+ # execute_atom :isDisplayed, element
204
+ execute :is_element_displayed, id: element.id
205
+ end
206
+
207
+ # For Appium
208
+ # override
209
+ def element_attribute(element, name)
210
+ # For W3C in Selenium Client
211
+ # execute_atom :getAttribute, element, name
212
+ execute :get_element_attribute, id: element.id, name: name
213
+ end
214
+
215
+ # For Appium
216
+ # override
217
+ def active_element
218
+ ::Appium::Core::Element.new self, element_id_from(execute(:get_active_element))
219
+ end
220
+ alias switch_to_active_element active_element
221
+
222
+ # For Appium
223
+ # override
224
+ def find_element_by(how, what, parent_ref = [])
225
+ how, what = convert_locator(how, what)
226
+
227
+ return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
228
+
229
+ parent_type, parent_id = parent_ref
230
+ id = case parent_type
231
+ when :element
232
+ execute :find_child_element, { id: parent_id }, { using: how, value: what.to_s }
233
+ when :shadow_root
234
+ execute :find_shadow_child_element, { id: parent_id }, { using: how, value: what.to_s }
235
+ else
236
+ execute :find_element, {}, { using: how, value: what.to_s }
237
+ end
238
+
239
+ ::Appium::Core::Element.new self, element_id_from(id)
240
+ end
241
+
242
+ # For Appium
243
+ # override
244
+ def find_elements_by(how, what, parent_ref = [])
245
+ how, what = convert_locator(how, what)
246
+
247
+ return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
248
+
249
+ parent_type, parent_id = parent_ref
250
+ ids = case parent_type
251
+ when :element
252
+ execute :find_child_elements, { id: parent_id }, { using: how, value: what.to_s }
253
+ when :shadow_root
254
+ execute :find_shadow_child_elements, { id: parent_id }, { using: how, value: what.to_s }
255
+ else
256
+ execute :find_elements, {}, { using: how, value: what.to_s }
257
+ end
258
+
259
+ ids.map { |id| ::Appium::Core::Element.new self, element_id_from(id) }
260
+ end
261
+
262
+ # For Appium
263
+ # @param [Hash] id The id which can get as a response from server
264
+ # @return [::Appium::Core::Element]
265
+ def convert_to_element(id)
266
+ ::Appium::Core::Element.new self, element_id_from(id)
267
+ end
268
+
269
+ # For Appium
270
+ # override
271
+ # called in 'extend DriverExtensions::HasNetworkConnection'
272
+ def network_connection
273
+ execute :get_network_connection
274
+ end
275
+
276
+ # For Appium
277
+ # override
278
+ # called in 'extend DriverExtensions::HasNetworkConnection'
279
+ def network_connection=(type)
280
+ execute :set_network_connection, {}, { parameters: { type: type } }
281
+ end
282
+
283
+ # For Appium
284
+ # No implementation for W3C webdriver module
285
+ # called in 'extend DriverExtensions::HasLocation'
286
+ def location
287
+ obj = execute(:get_location) || {}
288
+ ::Selenium::WebDriver::Location.new obj['latitude'], obj['longitude'], obj['altitude']
289
+ end
290
+
291
+ # For Appium
292
+ # No implementation for W3C webdriver module
293
+ def set_location(lat, lon, alt = 0.0, speed: nil, satellites: nil)
294
+ loc = { latitude: lat, longitude: lon, altitude: alt }
295
+ loc[:speed] = speed unless speed.nil?
296
+ loc[:satellites] = satellites unless satellites.nil?
297
+ execute :set_location, {}, { location: loc }
298
+ end
299
+
300
+ #
301
+ # logs
302
+ #
303
+ # For Appium
304
+ # No implementation for W3C webdriver module
305
+ def available_log_types
306
+ types = execute :get_available_log_types
307
+ Array(types).map(&:to_sym)
308
+ end
309
+
310
+ # For Appium
311
+ # No implementation for W3C webdriver module
312
+ def log(type)
313
+ data = execute :get_log, {}, { type: type.to_s }
314
+
315
+ Array(data).map do |l|
316
+ ::Selenium::WebDriver::LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
317
+ rescue KeyError
318
+ next
166
319
  end
320
+ end
167
321
 
168
- w3c_capabilities
322
+ # For Appium
323
+ def log_event(vendor, event)
324
+ execute :post_log_event, {}, { vendor: vendor, event: event }
169
325
  end
170
326
 
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?
327
+ # For Appium
328
+ def log_events(type = nil)
329
+ args = {}
330
+ args['type'] = type unless type.nil?
174
331
 
175
- ::Appium::Logger.warn "'forceMjsonwp' no longer works. Sending both W3C and MJSONWP capabilities" if force_mjsonwp
332
+ execute :get_log_events, {}, args
333
+ end
334
+
335
+ def viewport_screenshot
336
+ execute_script('mobile: viewportScreenshot')
337
+ end
176
338
 
177
- new_caps = add_appium_prefix(desired_capabilities)
178
- w3c_capabilities = ::Selenium::WebDriver::Remote::W3C::Capabilities.from_oss(new_caps)
339
+ def element_screenshot(element_id)
340
+ execute :take_element_screenshot, id: element_id
341
+ end
342
+
343
+ private
344
+
345
+ def unwrap_script_result(arg)
346
+ case arg
347
+ when Array
348
+ arg.map { |e| unwrap_script_result(e) }
349
+ when Hash
350
+ element_id = element_id_from(arg)
351
+ return ::Appium::Core::Element.new(self, element_id) if element_id
352
+
353
+ arg.each { |k, v| arg[k] = unwrap_script_result(v) }
354
+ else
355
+ arg
356
+ end
357
+ end
358
+
359
+ def element_id_from(id)
360
+ id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
361
+ end
179
362
 
180
- {
181
- desiredCapabilities: desired_capabilities,
182
- capabilities: {
183
- firstMatch: [w3c_capabilities]
184
- }
185
- }
363
+ # Don't convert locators for Appium in native context
364
+ def convert_locator(how, what)
365
+ # case how
366
+ # when 'class name'
367
+ # how = 'css selector'
368
+ # what = ".#{escape_css(what)}"
369
+ # when 'id'
370
+ # how = 'css selector'
371
+ # what = "##{escape_css(what)}"
372
+ # when 'name'
373
+ # how = 'css selector'
374
+ # what = "*[name='#{escape_css(what)}']"
375
+ # when 'tag name'
376
+ # how = 'css selector'
377
+ # end
378
+ #
379
+ # if what.is_a?(Hash)
380
+ # what = what.each_with_object({}) do |(h, w), hash|
381
+ # h, w = convert_locator(h.to_s, w)
382
+ # hash[h] = w
383
+ # end
384
+ # end
385
+
386
+ [how, what]
186
387
  end
187
388
  end # class Bridge
188
389
  end # class Base
@@ -19,10 +19,17 @@ module Appium
19
19
  # @private
20
20
  # @param [Hash] opts_caps Capabilities for Appium server. All capability keys are converted to lowerCamelCase when
21
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
22
+ # @return [::Selenium::WebDriver::Remote::Capabilities] Return instance of Appium::Core::Base::Capabilities
23
+ # inherited ::Selenium::WebDriver::Remote::Capabilities
24
24
  def self.create_capabilities(opts_caps = {})
25
- ::Selenium::WebDriver::Remote::W3C::Capabilities.new(opts_caps)
25
+ # TODO: Move to 'Options' way instead of 'Capabilities'.
26
+ # Selenium 5 will have Options instead of 'Capabilities'.
27
+ # https://github.com/SeleniumHQ/selenium/blob/trunk/rb/lib/selenium/webdriver/common/options.rb
28
+ # Then, Ruby client also shoud move to the Options way.
29
+ # Appium's capabilities could change by depending on Appium versions. So it does not have
30
+ # standard options like chrome and firefox etc. So, the implementation should differ from
31
+ # other browsers. But here should inherit `Options` to follow Selenium.
32
+ ::Selenium::WebDriver::Remote::Capabilities.new(opts_caps)
26
33
  end
27
34
  end
28
35
  end