appium_lib_core 4.5.0 → 5.0.0.beta4

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/unittest.yml +2 -2
  3. data/CHANGELOG.md +12 -5
  4. data/README.md +2 -1
  5. data/appium_lib_core.gemspec +4 -4
  6. data/lib/appium_lib_core.rb +2 -5
  7. data/lib/appium_lib_core/android/device/auth_finger_print.rb +2 -1
  8. data/lib/appium_lib_core/common/base.rb +0 -3
  9. data/lib/appium_lib_core/common/base/bridge.rb +284 -89
  10. data/lib/appium_lib_core/common/base/capabilities.rb +3 -3
  11. data/lib/appium_lib_core/common/base/{command.rb → device_ime.rb} +32 -7
  12. data/lib/appium_lib_core/common/base/driver.rb +128 -70
  13. data/lib/appium_lib_core/common/base/driver_settings.rb +51 -0
  14. data/lib/appium_lib_core/common/base/has_network_connection.rb +56 -0
  15. data/lib/appium_lib_core/common/base/rotable.rb +1 -1
  16. data/lib/appium_lib_core/common/base/search_context.rb +9 -4
  17. data/lib/appium_lib_core/common/command.rb +259 -4
  18. data/lib/appium_lib_core/common/device/keyevent.rb +4 -4
  19. data/lib/appium_lib_core/common/error.rb +4 -1
  20. data/lib/appium_lib_core/common/log.rb +4 -1
  21. data/lib/appium_lib_core/device.rb +1 -5
  22. data/lib/appium_lib_core/driver.rb +3 -4
  23. data/lib/appium_lib_core/{patch.rb → element.rb} +2 -7
  24. data/lib/appium_lib_core/ios/uiautomation/patch.rb +1 -1
  25. data/lib/appium_lib_core/version.rb +2 -2
  26. data/release_notes.md +14 -0
  27. data/script/commands.rb +3 -37
  28. metadata +21 -30
  29. data/lib/appium_lib_core/common/base/bridge/mjsonwp.rb +0 -91
  30. data/lib/appium_lib_core/common/base/bridge/w3c.rb +0 -248
  31. data/lib/appium_lib_core/common/command/common.rb +0 -110
  32. data/lib/appium_lib_core/common/command/mjsonwp.rb +0 -28
  33. data/lib/appium_lib_core/common/command/w3c.rb +0 -56
@@ -19,10 +19,10 @@ 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
+ ::Selenium::WebDriver::Remote::Capabilities.new(opts_caps)
26
26
  end
27
27
  end
28
28
  end
@@ -15,10 +15,35 @@
15
15
  module Appium
16
16
  module Core
17
17
  class Base
18
- module Commands
19
- OSS = ::Selenium::WebDriver::Remote::OSS::Bridge::COMMANDS.freeze
20
- W3C = ::Selenium::WebDriver::Remote::W3C::Bridge::COMMANDS.freeze
21
- end # module Commands
22
- end # module Base
23
- end # module Core
24
- end # module Appium
18
+ #
19
+ # @api private
20
+ #
21
+ class DeviceIME
22
+ # @private this class is private
23
+ def initialize(bridge)
24
+ @bridge = bridge
25
+ end
26
+
27
+ def activate(ime_name)
28
+ @bridge.ime_activate(ime_name)
29
+ end
30
+
31
+ def available_engines
32
+ @bridge.ime_available_engines
33
+ end
34
+
35
+ def active_engine
36
+ @bridge.ime_active_engine
37
+ end
38
+
39
+ def activated?
40
+ @bridge.ime_activated
41
+ end
42
+
43
+ def deactivate
44
+ @bridge.ime_deactivate
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -13,11 +13,14 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require 'base64'
16
+ require_relative 'device_ime'
17
+ require_relative 'driver_settings'
16
18
  require_relative 'search_context'
17
19
  require_relative 'screenshot'
18
20
  require_relative 'rotable'
19
21
  require_relative 'remote_status'
20
22
  require_relative 'has_location'
23
+ require_relative 'has_network_connection'
21
24
 
22
25
  module Appium
23
26
  module Core
@@ -27,31 +30,63 @@ module Appium
27
30
  include ::Selenium::WebDriver::DriverExtensions::HasSessionId
28
31
  include ::Selenium::WebDriver::DriverExtensions::HasRemoteStatus
29
32
  include ::Selenium::WebDriver::DriverExtensions::HasWebStorage
30
- include ::Selenium::WebDriver::DriverExtensions::HasNetworkConnection
31
- include ::Selenium::WebDriver::DriverExtensions::HasTouchScreen
32
33
 
33
34
  include ::Appium::Core::Base::Rotatable
34
35
  include ::Appium::Core::Base::SearchContext
35
36
  include ::Appium::Core::Base::TakesScreenshot
36
37
  include ::Appium::Core::Base::HasRemoteStatus
37
38
  include ::Appium::Core::Base::HasLocation
39
+ include ::Appium::Core::Base::HasNetworkConnection
40
+
41
+ private
38
42
 
39
43
  # Private API.
40
44
  # Do not use this for general use. Used by flutter driver to get bridge for creating a new element
41
45
  attr_reader :bridge
42
46
 
43
- def initialize(opts = {})
44
- listener = opts.delete(:listener)
45
- @bridge = ::Appium::Core::Base::Bridge.handshake(**opts)
46
- super(@bridge, listener: listener)
47
+ def initialize(bridge: nil, listener: nil, **opts)
48
+ super
47
49
  end
48
50
 
49
- # Get the dialect value
50
- # @return [:oss|:w3c]
51
- def dialect
52
- @bridge.dialect
51
+ # Implements protocol handshake which:
52
+ #
53
+ # 1. Creates session with driver.
54
+ # 2. Sniffs response.
55
+ # 3. Based on the response, understands which dialect we should use.
56
+ #
57
+ # @return [::Appium::Core::Base::Bridge]
58
+ #
59
+ def create_bridge(**opts)
60
+ # TODO: probably Appium does not need this.
61
+ opts[:url] ||= service_url(opts)
62
+ caps = opts.delete(:capabilities)
63
+ # NOTE: This is deprecated
64
+ cap_array = caps.is_a?(Hash) ? [caps] : Array(caps)
65
+
66
+ desired_capabilities = opts.delete(:desired_capabilities)
67
+ if desired_capabilities
68
+ if desired_capabilities.is_a?(Hash)
69
+ desired_capabilities = ::Selenium::WebDriver::Remote::Capabilities(desired_capabilities)
70
+ end
71
+ cap_array << desired_capabilities
72
+ end
73
+
74
+ options = opts.delete(:options)
75
+ cap_array << options if options
76
+
77
+ capabilities = generate_capabilities(cap_array)
78
+
79
+ bridge_opts = { http_client: opts.delete(:http_client), url: opts.delete(:url) }
80
+ raise ::Appium::Core::Error::ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
81
+
82
+ bridge = ::Appium::Core::Base::Bridge.new(**bridge_opts)
83
+
84
+ bridge.create_session(capabilities)
85
+ bridge
53
86
  end
54
87
 
88
+ public
89
+
55
90
  # Update +server_url+ and HTTP clients following this arguments, protocol, host, port and path.
56
91
  # After this method, +@bridge.http+ will be a new instance following them instead of +server_url+ which is
57
92
  # set before creating session.
@@ -76,6 +111,75 @@ module Appium
76
111
  path: path)
77
112
  end
78
113
 
114
+ AVAILABLE_METHODS = [
115
+ :get, :head, :post, :put, :delete,
116
+ :connect, :options, :trace, :patch
117
+ ].freeze
118
+ # Define a new custom method to the driver so that you can define your own method for
119
+ # drivers/plugins in Appium 2.0. Appium 2.0 and its custom drivers/plugins allow you
120
+ # to define custom commands that are not part of W3C spec.
121
+ #
122
+ # @param [Symbol] method HTTP request method as https://www.w3.org/TR/webdriver/#endpoints
123
+ # @param [string] url The url to URL template as https://www.w3.org/TR/webdriver/#endpoints.
124
+ # +:session_id+ is the placeholder of 'session id'.
125
+ # Other place holders can be specified with +:+ prefix like +:id+.
126
+ # Then, the +:id+ will be replaced with a given value as the seconds argument of +execute+
127
+ # @param [Symbol] name The name of method that is called as the driver instance method.
128
+ # @param [Proc] block The block to involve as the method
129
+ # @raise [ArgumentError] If the given +name+ is already defined or +method+ are invalid value.
130
+ #
131
+ # @example
132
+ #
133
+ # @driver.add_command(
134
+ # method: :get,
135
+ # url: 'session/:session_id/path/to/custom/url',
136
+ # name: :test_command
137
+ # )
138
+ # # Send a GET request to 'session/<session id>/path/to/custom/url'
139
+ # @driver.test_command
140
+ #
141
+ #
142
+ # @driver.add_command(
143
+ # method: :post,
144
+ # url: 'session/:session_id/path/to/custom/url',
145
+ # name: :test_command
146
+ # ) do
147
+ # def test_command(argument)
148
+ # execute(:test_command, {}, { dummy: argument })
149
+ # end
150
+ # end
151
+ # # Send a POST request to 'session/<session id>/path/to/custom/url'
152
+ # # with body "{ dummy: 1 }" as JSON object. "1" is the argument.
153
+ # # ':session_id' in the given 'url' is replaced with current 'session id'.
154
+ # @driver.test_command(1)
155
+ #
156
+ # @driver.add_command(
157
+ # method: :post,
158
+ # url: 'session/:session_id/element/:id/custom/action',
159
+ # name: :test_action_command
160
+ # ) do
161
+ # def test_action_command(element_id, action)
162
+ # execute(:test_action_command, {id: element_id}, { dummy_action: action })
163
+ # end
164
+ # end
165
+ # # Send a POST request to 'session/<session id>/element/<element id>/custom/action'
166
+ # # with body "{ dummy_action: #{action} }" as JSON object. "action" is the seconds argument.
167
+ # # ':session_id' in the given url is replaced with current 'session id'.
168
+ # # ':id' in the given url is replaced with the given 'element_id'.
169
+ # e = @driver.find_element :accessibility_id, 'an element'
170
+ # @driver.test_action_command(e.ref, 'action')
171
+ #
172
+ def add_command(method:, url:, name:, &block)
173
+ unless AVAILABLE_METHODS.include? method
174
+ raise ::Appium::Core::Error::ArgumentError, "Available method is either #{AVAILABLE_METHODS}"
175
+ end
176
+
177
+ # TODO: Remove this logger before Appium 2.0 release
178
+ ::Appium::Logger.info '[Experimental] this method is experimental for Appium 2.0. This interface may change.'
179
+
180
+ @bridge.add_command method: method, url: url, name: name, &block
181
+ end
182
+
79
183
  ### Methods for Appium
80
184
 
81
185
  # Lock the device
@@ -158,22 +262,6 @@ module Appium
158
262
  end
159
263
  alias type send_keys
160
264
 
161
- class DriverSettings
162
- # @private this class is private
163
- def initialize(bridge)
164
- @bridge = bridge
165
- end
166
-
167
- def get
168
- @bridge.get_settings
169
- end
170
-
171
- def update(settings)
172
- @bridge.update_settings(settings)
173
- end
174
- end
175
- private_constant :DriverSettings
176
-
177
265
  # Returns an instance of DriverSettings to call get/update.
178
266
  #
179
267
  # @example
@@ -182,7 +270,7 @@ module Appium
182
270
  # @driver.settings.update('allowInvisibleElements': true)
183
271
  #
184
272
  def settings
185
- @driver_settings ||= DriverSettings.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
273
+ @settings ||= DriverSettings.new(@bridge)
186
274
  end
187
275
 
188
276
  # Get appium Settings for current test session.
@@ -204,8 +292,8 @@ module Appium
204
292
  #
205
293
  # @example
206
294
  #
207
- # @driver.update_settings('allowInvisibleElements': true)
208
- # @driver.settings.update('allowInvisibleElements': true)
295
+ # @driver.update_settings({ 'allowInvisibleElements': true })
296
+ # @driver.settings.update({ 'allowInvisibleElements': true })
209
297
  # @driver.settings = { 'allowInvisibleElements': true }
210
298
  #
211
299
  def settings=(value)
@@ -213,34 +301,6 @@ module Appium
213
301
  end
214
302
  alias update_settings settings=
215
303
 
216
- class DeviceIME
217
- # @private this class is private
218
- def initialize(bridge)
219
- @bridge = bridge
220
- end
221
-
222
- def activate(ime_name)
223
- @bridge.ime_activate(ime_name)
224
- end
225
-
226
- def available_engines
227
- @bridge.ime_available_engines
228
- end
229
-
230
- def active_engine
231
- @bridge.ime_active_engine
232
- end
233
-
234
- def activated?
235
- @bridge.ime_activated
236
- end
237
-
238
- def deactivate
239
- @bridge.ime_deactivate
240
- end
241
- end
242
- private_constant :DeviceIME
243
-
244
304
  # Returns an instance of DeviceIME
245
305
  #
246
306
  # @return [Appium::Core::Base::Driver::DeviceIME]
@@ -254,7 +314,7 @@ module Appium
254
314
  # @driver.ime.deactivate #=> Deactivate current IME engine
255
315
  #
256
316
  def ime
257
- @device_ime ||= DeviceIME.new(@bridge) # rubocop:disable Naming/MemoizedInstanceVariableName
317
+ @ime ||= DeviceIME.new(@bridge)
258
318
  end
259
319
 
260
320
  # Android only. Make an engine that is available active.
@@ -817,7 +877,7 @@ module Appium
817
877
  # @driver.perform_actions [f1, f2] #=> 'nil' if the action succeed
818
878
  #
819
879
  def perform_actions(data)
820
- raise ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
880
+ raise ::Appium::Core::Error::ArgumentError, "'#{data}' must be Array" unless data.is_a? Array
821
881
 
822
882
  @bridge.send_actions data.map(&:encode).compact
823
883
  data.each(&:clear_actions)
@@ -887,16 +947,15 @@ module Appium
887
947
  # Retrieve the capabilities of the specified session.
888
948
  # It's almost same as +@driver.capabilities+ but you can get more details.
889
949
  #
890
- # @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::W3C::Capabilities]
950
+ # @return [Selenium::WebDriver::Remote::Capabilities, Selenium::WebDriver::Remote::Capabilities]
891
951
  #
892
952
  # @example
893
953
  # @driver.session_capabilities
894
954
  #
895
955
  # #=> uiautomator2
896
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fa38dae1360
956
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fa38dae1360
897
957
  # # @capabilities=
898
- # # {:proxy=>nil,
899
- # # :browser_name=>nil,
958
+ # # {:browser_name=>nil,
900
959
  # # :browser_version=>nil,
901
960
  # # :platform_name=>"android",
902
961
  # # :page_load_strategy=>nil,
@@ -943,10 +1002,9 @@ module Appium
943
1002
  # # "viewportRect"=>{"left"=>0, "top"=>63, "width"=>1080, "height"=>1731}}>
944
1003
  # #
945
1004
  # #=> XCUITest
946
- # # <Selenium::WebDriver::Remote::W3C::Capabilities:0x007fb15dc01370
1005
+ # # <Selenium::WebDriver::Remote::Capabilities:0x007fb15dc01370
947
1006
  # # @capabilities=
948
- # # {:proxy=>nil,
949
- # # :browser_name=>"UICatalog",
1007
+ # # {:browser_name=>"UICatalog",
950
1008
  # # :browser_version=>nil,
951
1009
  # # :platform_name=>"ios",
952
1010
  # # :page_load_strategy=>nil,
@@ -1027,7 +1085,7 @@ module Appium
1027
1085
  #
1028
1086
  # @param [String] img_path A path to a partial image you'd like to find
1029
1087
  #
1030
- # @return [::Selenium::WebDriver::Element]
1088
+ # @return [::Appium::Core::Element]
1031
1089
  #
1032
1090
  # @example
1033
1091
  #
@@ -1097,14 +1155,14 @@ module Appium
1097
1155
  @bridge.execute_driver(script: script, type: type, timeout_ms: timeout_ms)
1098
1156
  end
1099
1157
 
1100
- # Convert vanilla element response to ::Selenium::WebDriver::Element
1158
+ # Convert vanilla element response to ::Appium::Core::Element
1101
1159
  #
1102
1160
  # @param [Hash] id The id which can get as a response from server
1103
- # @return [::Selenium::WebDriver::Element]
1161
+ # @return [::Appium::Core::Element]
1104
1162
  #
1105
1163
  # @example
1106
1164
  # response = {"element-6066-11e4-a52e-4f735466cecf"=>"xxxx", "ELEMENT"=>"xxxx"}
1107
- # ele = @driver.convert_to_element(response) #=> ::Selenium::WebDriver::Element
1165
+ # ele = @driver.convert_to_element(response) #=> ::Appium::Core::Element
1108
1166
  # ele.rect #=> Can get the rect of the element
1109
1167
  #
1110
1168
  def convert_to_element(id)
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Appium
16
+ module Core
17
+ class Base
18
+ #
19
+ # @api private
20
+ #
21
+ class DriverSettings
22
+ # @private this class is private
23
+ def initialize(bridge)
24
+ @bridge = bridge
25
+ end
26
+
27
+ # Get appium Settings for current test session.
28
+ #
29
+ # @example
30
+ #
31
+ # @driver.settings.get
32
+ #
33
+ def get
34
+ @bridge.get_settings
35
+ end
36
+
37
+ # Update Appium Settings for current test session
38
+ #
39
+ # @param [Hash] settings Settings to update, keys are settings, values to value to set each setting to
40
+ #
41
+ # @example
42
+ #
43
+ # @driver.settings.update({'allowInvisibleElements': true})
44
+ #
45
+ def update(settings)
46
+ @bridge.update_settings(settings)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Appium
16
+ module Core
17
+ class Base
18
+ #
19
+ # @api private
20
+ #
21
+ module HasNetworkConnection
22
+ def network_connection_type
23
+ connection_value = @bridge.network_connection
24
+
25
+ connection_type = values_to_type[connection_value]
26
+
27
+ # In case the connection type is not recognized return the
28
+ # connection value.
29
+ connection_type || connection_value
30
+ end
31
+
32
+ def network_connection_type=(connection_type)
33
+ raise ::Appium::Core::Error::ArgumentError, 'Invalid connection type' unless valid_type? connection_type
34
+
35
+ connection_value = type_to_values[connection_type]
36
+
37
+ @bridge.network_connection = connection_value
38
+ end
39
+
40
+ private
41
+
42
+ def type_to_values
43
+ { airplane_mode: 1, wifi: 2, data: 4, all: 6, none: 0 }
44
+ end
45
+
46
+ def values_to_type
47
+ type_to_values.invert
48
+ end
49
+
50
+ def valid_type?(type)
51
+ type_to_values.keys.include? type
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end