appium_lib_core 4.5.0 → 5.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
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