appium_lib_core 5.0.0 → 6.3.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +140 -0
  3. data/README.md +55 -10
  4. data/appium_lib_core.gemspec +5 -5
  5. data/lib/appium_lib_core/common/base/bridge.rb +54 -40
  6. data/lib/appium_lib_core/common/base/capabilities.rb +8 -16
  7. data/lib/appium_lib_core/common/base/driver.rb +68 -40
  8. data/lib/appium_lib_core/common/base/has_location.rb +10 -10
  9. data/lib/appium_lib_core/common/base/http_default.rb +1 -1
  10. data/lib/appium_lib_core/common/base/rotable.rb +11 -3
  11. data/lib/appium_lib_core/common/base/search_context.rb +1 -2
  12. data/lib/appium_lib_core/common/command.rb +4 -2
  13. data/lib/appium_lib_core/common/device/app_management.rb +8 -14
  14. data/lib/appium_lib_core/common/device/value.rb +1 -3
  15. data/lib/appium_lib_core/common/error.rb +0 -4
  16. data/lib/appium_lib_core/driver.rb +170 -62
  17. data/lib/appium_lib_core/support/event_firing_bridge.rb +57 -0
  18. data/lib/appium_lib_core/version.rb +2 -2
  19. data/lib/appium_lib_core/{ios/uiautomation/bridge.rb → windows/device/app_management.rb} +20 -12
  20. data/lib/appium_lib_core/windows/device.rb +2 -0
  21. data/lib/appium_lib_core.rb +19 -5
  22. metadata +21 -34
  23. data/.github/ISSUE_TEMPLATE/issue-report.md +0 -29
  24. data/.github/contributing.md +0 -26
  25. data/.github/dependabot.yml +0 -8
  26. data/.github/issue_template.md +0 -20
  27. data/.github/workflows/unittest.yml +0 -67
  28. data/.gitignore +0 -18
  29. data/.rubocop.yml +0 -146
  30. data/azure-pipelines.yml +0 -15
  31. data/ci-jobs/functional/android_setup.yml +0 -3
  32. data/ci-jobs/functional/ios_setup.yml +0 -7
  33. data/ci-jobs/functional/publish_test_result.yml +0 -18
  34. data/ci-jobs/functional/run_appium.yml +0 -25
  35. data/ci-jobs/functional/start-emulator.sh +0 -26
  36. data/ci-jobs/functional_test.yml +0 -298
  37. data/docs/mobile_command.md +0 -34
  38. data/lib/appium_lib_core/ios/uiautomation/device.rb +0 -44
  39. data/lib/appium_lib_core/ios/uiautomation/patch.rb +0 -34
  40. data/lib/appium_lib_core/ios.rb +0 -20
  41. data/release_notes.md +0 -932
  42. data/script/commands.rb +0 -166
@@ -29,7 +29,6 @@ module Appium
29
29
  class Driver < ::Selenium::WebDriver::Driver
30
30
  include ::Selenium::WebDriver::DriverExtensions::UploadsFiles
31
31
  include ::Selenium::WebDriver::DriverExtensions::HasSessionId
32
- include ::Selenium::WebDriver::DriverExtensions::HasRemoteStatus
33
32
  include ::Selenium::WebDriver::DriverExtensions::HasWebStorage
34
33
 
35
34
  include ::Appium::Core::Base::Rotatable
@@ -41,36 +40,54 @@ module Appium
41
40
 
42
41
  include ::Appium::Core::Waitable
43
42
 
44
- private
45
-
46
43
  # Private API.
47
44
  # Do not use this for general use. Used by flutter driver to get bridge for creating a new element
48
45
  attr_reader :bridge
49
46
 
50
47
  def initialize(bridge: nil, listener: nil, **opts)
48
+ original_opts = opts.dup
49
+
51
50
  # For ::Appium::Core::Waitable
52
51
  @wait_timeout = opts.delete(:wait_timeout)
53
52
  @wait_interval = opts.delete(:wait_interval)
54
53
 
55
- # For logging.
56
- # TODO: Remove when appium core no longer uses this in this bridge.
57
- @automation_name = opts.delete(:automation_name)
54
+ # Selenium WebDriver attributes
55
+ @devtools = nil
56
+ @bidi = nil
58
57
 
59
- super
58
+ # in the selenium webdriver as well
59
+ bridge ||= create_bridge(**opts)
60
+ add_extensions(bridge.browser)
61
+ @bridge = listener ? ::Appium::Support::EventFiringBridge.new(bridge, listener, **original_opts) : bridge
60
62
  end
61
63
 
64
+ private
65
+
62
66
  # Create a proper bridge instance.
63
67
  #
64
68
  # @return [::Appium::Core::Base::Bridge]
65
69
  #
66
70
  def create_bridge(**opts)
71
+ # for a new session request
67
72
  capabilities = opts.delete(:capabilities)
68
73
  bridge_opts = { http_client: opts.delete(:http_client), url: opts.delete(:url) }
74
+
75
+ # for attaching to an existing session
76
+ session_id = opts.delete(:existing_session_id)
77
+ automation_name = opts.delete(:automation_name)
78
+ platform_name = opts.delete(:platform_name)
79
+
69
80
  raise ::Appium::Core::Error::ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
70
81
 
71
82
  bridge = ::Appium::Core::Base::Bridge.new(**bridge_opts)
72
83
 
73
- bridge.create_session(capabilities)
84
+ if session_id.nil?
85
+ bridge.create_session(capabilities)
86
+ else
87
+ # attach to the existing session id
88
+ bridge.attach_to(session_id, platform_name, automation_name)
89
+ end
90
+
74
91
  bridge
75
92
  end
76
93
 
@@ -181,6 +198,31 @@ module Appium
181
198
 
182
199
  ### Methods for Appium
183
200
 
201
+ # Perform 'key' actions for W3C module.
202
+ # Generate +key+ pointer action here and users can use this via +driver.key_action+
203
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/W3CActionBuilder.html
204
+ # - https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/KeyActions.html
205
+ #
206
+ # The pointer type is 'key' by default in the Appium Ruby client.
207
+ # +driver.action+ in Appium Ruby client has 'pointer' action by default.
208
+ # This method is a shortcut to set 'key' type.
209
+ # Hense this method is equal to +driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')])+
210
+ # as below example.
211
+ #
212
+ # @example
213
+ #
214
+ # element = @driver.find_element(:id, "some id")
215
+ # @driver.key_action.send_key('hiあ').perform # The 'send_key' is a part of 'KeyActions'
216
+ # # is equal to:
217
+ # # @driver.action(devices: [::Selenium::WebDriver::Interactions.key('keyboard')]).send_keys('hiあ').perform
218
+ #
219
+ def key_action(async: false)
220
+ @bridge.action(
221
+ async: async,
222
+ devices: [::Selenium::WebDriver::Interactions.key('keyboard')]
223
+ )
224
+ end
225
+
184
226
  # Lock the device
185
227
  # @return [String]
186
228
  #
@@ -542,12 +584,6 @@ module Appium
542
584
  # @driver.launch_app
543
585
  #
544
586
  def launch_app
545
- # TODO: Define only in Windows module when ruby_lib_core removes this method
546
- if @automation_name != :windows
547
- ::Appium::Logger.warn(
548
- '[DEPRECATION] launch_app is deprecated. Please use activate_app instead.'
549
- )
550
- end
551
587
  @bridge.launch_app
552
588
  end
553
589
 
@@ -559,12 +595,6 @@ module Appium
559
595
  # @driver.close_app
560
596
  #
561
597
  def close_app
562
- # TODO: Define only in Windows module when ruby_lib_core removes this method
563
- if @automation_name != :windows
564
- ::Appium::Logger.warn(
565
- '[DEPRECATION] close_app is deprecated. Please use terminate_app instead.'
566
- )
567
- end
568
598
  @bridge.close_app
569
599
  end
570
600
 
@@ -609,7 +639,9 @@ module Appium
609
639
  @bridge.background_app(duration)
610
640
  end
611
641
 
612
- # Install the given app onto the device
642
+ # Install the given app onto the device.
643
+ # Each options can be snake-case or camel-case. Snake-cases will be converted to camel-case
644
+ # as options value.
613
645
  #
614
646
  # @param [String] path The absolute local path or remote http URL to an .ipa or .apk file,
615
647
  # or a .zip containing one of these.
@@ -623,26 +655,26 @@ module Appium
623
655
  # @param [Boolean] grant_permissions Only for Android. whether to automatically grant application permissions
624
656
  # on Android 6+ after the installation completes. +false+ by default
625
657
  #
658
+ # Other parameters such as https://github.com/appium/appium-xcuitest-driver#mobile-installapp also can be set.
659
+ # Then, arguments in snake case will be camel case as its request parameters.
660
+ #
626
661
  # @example
627
662
  #
628
663
  # @driver.install_app("/path/to/test.apk")
629
664
  # @driver.install_app("/path/to/test.apk", replace: true, timeout: 20000, allow_test_packages: true,
630
665
  # use_sdcard: false, grant_permissions: false)
666
+ # @driver.install_app("/path/to/test.ipa", timeoutMs: 20000)
631
667
  #
632
- def install_app(path,
633
- replace: nil,
634
- timeout: nil,
635
- allow_test_packages: nil,
636
- use_sdcard: nil,
637
- grant_permissions: nil)
638
- @bridge.install_app(path,
639
- replace: replace,
640
- timeout: timeout,
641
- allow_test_packages: allow_test_packages,
642
- use_sdcard: use_sdcard,
643
- grant_permissions: grant_permissions)
668
+ def install_app(path, **options)
669
+ options = options.transform_keys { |key| key.to_s.gsub(/_./) { |v| v[1].upcase } } unless options.nil?
670
+ @bridge.install_app(path, options)
644
671
  end
645
672
 
673
+ # def capitalize(s)
674
+ # chars =
675
+ # chars[1:].map(&:capitalize).join
676
+ # end
677
+
646
678
  # @param [Strong] app_id BundleId for iOS or package name for Android
647
679
  # @param [Boolean] keep_data Only for Android. Whether to keep application data and caches after it is uninstalled.
648
680
  # +false+ by default
@@ -924,8 +956,6 @@ module Appium
924
956
  # # "appPackage"=>"io.appium.android.apis",
925
957
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
926
958
  # # "someCapability"=>"some_capability",
927
- # # "unicodeKeyboard"=>true,
928
- # # "resetKeyboard"=>true},
929
959
  # # "automationName"=>"uiautomator2",
930
960
  # # "app"=>"/path/to/app/api.apk.zip",
931
961
  # # "platformVersion"=>"8.1.0",
@@ -933,8 +963,6 @@ module Appium
933
963
  # # "appPackage"=>"io.appium.android.apis",
934
964
  # # "appActivity"=>"io.appium.android.apis.ApiDemos",
935
965
  # # "someCapability"=>"some_capability",
936
- # # "unicodeKeyboard"=>true,
937
- # # "resetKeyboard"=>true,
938
966
  # # "deviceUDID"=>"emulator-5554",
939
967
  # # "deviceScreenSize"=>"1080x1920",
940
968
  # # "deviceScreenDensity"=>420,
@@ -1021,7 +1049,7 @@ module Appium
1021
1049
 
1022
1050
  # @since Appium 1.8.2
1023
1051
  # Return an element if current view has a partial image. The logic depends on template matching by OpenCV.
1024
- # {https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md image-comparison}
1052
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1025
1053
  #
1026
1054
  # You can handle settings for the comparision following below.
1027
1055
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
@@ -1043,14 +1071,14 @@ module Appium
1043
1071
 
1044
1072
  # @since Appium 1.8.2
1045
1073
  # Return elements if current view has a partial image. The logic depends on template matching by OpenCV.
1046
- # {https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md image-comparison}
1074
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
1047
1075
  #
1048
1076
  # You can handle settings for the comparision following below.
1049
1077
  # {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 device-settings}
1050
1078
  #
1051
1079
  # @param [String] img_path A path to a partial image you'd like to find
1052
1080
  #
1053
- # @return [Array<Selenium::WebDriver::Element>]
1081
+ # @return [Array<::Appium::Core::Element>]
1054
1082
  #
1055
1083
  # @example
1056
1084
  #
@@ -21,11 +21,11 @@ module Appium
21
21
  module HasLocation
22
22
  # Get the location of the device.
23
23
  #
24
- # @return [::Selenium::WebDriver::Location]
24
+ # @return [::Appium::Location]
25
25
  #
26
26
  # @example
27
27
  #
28
- # driver.location #=> ::Selenium::WebDriver::Location.new(10, 10, 10)
28
+ # driver.location #=> ::Appium::Location.new(10, 10, 10)
29
29
  #
30
30
  def location
31
31
  @bridge.location
@@ -33,15 +33,15 @@ module Appium
33
33
 
34
34
  # Set the location of the device.
35
35
  #
36
- # @param [::Selenium::WebDriver::Location] location Set the location.
36
+ # @param [::Appium::Location] location Set the location.
37
37
  #
38
38
  # @example
39
39
  #
40
- # driver.location = ::Selenium::WebDriver::Location.new(10, 10, 10)
40
+ # driver.location = ::Appium::Location.new(10, 10, 10)
41
41
  #
42
42
  def location=(location)
43
- unless location.is_a?(::Selenium::WebDriver::Location)
44
- raise TypeError, "expected #{::Selenium::WebDriver::Location}, got #{location.inspect}:#{location.class}"
43
+ unless location.is_a?(::Appium::Location)
44
+ raise TypeError, "expected #{::Appium::Location}, got #{location.inspect}:#{location.class}"
45
45
  end
46
46
 
47
47
  @bridge.set_location location.latitude, location.longitude, location.altitude
@@ -56,17 +56,17 @@ module Appium
56
56
  # in meters/second @since Appium 1.21.0 and in knots for emulators @since Appium 1.22.0.
57
57
  # @param [String, Number] satellites Sets the count of geo satellites being tracked in range 1..12 @since Appium 1.22.0.
58
58
  # This number is respected on Emulators.
59
- # @param [::Selenium::WebDriver::Location]
59
+ # @param [::Appium::Location]
60
60
  #
61
61
  # @example
62
62
  #
63
- # driver.location = ::Selenium::WebDriver::Location.new(10, 10, 10)
63
+ # driver.location = ::Appium::Location.new(10, 10, 10)
64
64
  #
65
65
  def set_location(latitude, longitude, altitude, speed: nil, satellites: nil)
66
66
  if speed.nil? && satellites.nil?
67
- self.location = ::Selenium::WebDriver::Location.new(Float(latitude), Float(longitude), Float(altitude))
67
+ self.location = ::Appium::Location.new(Float(latitude), Float(longitude), Float(altitude))
68
68
  else
69
- loc = ::Selenium::WebDriver::Location.new(Float(latitude), Float(longitude), Float(altitude))
69
+ loc = ::Appium::Location.new(Float(latitude), Float(longitude), Float(altitude))
70
70
 
71
71
  speed = Float(speed) unless speed.nil?
72
72
  satellites = Integer(satellites) unless satellites.nil?
@@ -37,7 +37,7 @@ module Appium
37
37
  attr_reader :additional_headers
38
38
 
39
39
  # override
40
- def initialize(open_timeout: nil, read_timeout: nil) # rubocop:disable Lint/MissingSuper
40
+ def initialize(open_timeout: nil, read_timeout: nil)
41
41
  @open_timeout = open_timeout
42
42
  @read_timeout = read_timeout
43
43
  @additional_headers = {}
@@ -20,12 +20,18 @@ module Appium
20
20
  #
21
21
 
22
22
  module Rotatable
23
- ORIENTATIONS = %i[landscape portrait].freeze
23
+ ORIENTATIONS = %i[
24
+ landscape
25
+ portrait
26
+ uia_device_orientation_landscaperight
27
+ uia_device_orientation_portrait_upsidedown
28
+ ].freeze
24
29
 
25
30
  #
26
31
  # Change the screen orientation
27
32
  #
28
- # @param [:landscape, :portrait] orientation
33
+ # @param [:landscape, :portrait,
34
+ # :uia_device_orientation_landscaperight, :uia_device_orientation_portrait_upsidedown] orientation
29
35
  #
30
36
  #
31
37
  def rotation=(orientation)
@@ -36,11 +42,13 @@ module Appium
36
42
  bridge.screen_orientation = orientation.to_s.upcase
37
43
  end
38
44
  alias rotate rotation=
45
+ alias orientation= rotation=
39
46
 
40
47
  #
41
48
  # Get the current screen orientation
42
49
  #
43
- # @return [:landscape, :portrait] orientation
50
+ # @return [:landscape, :portrait,
51
+ # :uia_device_orientation_landscaperight, :uia_device_orientation_portrait_upsidedown] orientation
44
52
  #
45
53
  # @api public
46
54
  #
@@ -28,7 +28,6 @@ module Appium
28
28
  data_matcher: '-android datamatcher', # Available in Espresso
29
29
  view_matcher: '-android viewmatcher', # Available in Espresso
30
30
  # iOS
31
- uiautomation: '-ios uiautomation',
32
31
  predicate: '-ios predicate string',
33
32
  class_chain: '-ios class chain',
34
33
  # Windows with windows prefix
@@ -48,7 +47,7 @@ module Appium
48
47
  #
49
48
  # == Find with image
50
49
  # Return an element if current view has a partial image. The logic depends on template matching by OpenCV.
51
- # {https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/image-comparison.md image-comparison}
50
+ # {https://github.com/appium/appium/blob/1.x/docs/en/writing-running-appium/image-comparison.md image-comparison}
52
51
  #
53
52
  # You can handle settings for the comparision following {https://github.com/appium/appium-base-driver/blob/master/lib/basedriver/device-settings.js#L6 here}
54
53
  #
@@ -70,11 +70,15 @@ module Appium
70
70
  find_elements: [:post, 'session/:session_id/elements'],
71
71
  find_child_element: [:post, 'session/:session_id/element/:id/element'],
72
72
  find_child_elements: [:post, 'session/:session_id/element/:id/elements'],
73
+ find_shadow_child_element: [:post, 'session/:session_id/shadow/:id/element'],
74
+ find_shadow_child_elements: [:post, 'session/:session_id/shadow/:id/elements'],
73
75
  get_active_element: [:get, 'session/:session_id/element/active'],
74
76
  is_element_selected: [:get, 'session/:session_id/element/:id/selected'],
75
77
  get_element_attribute: [:get, 'session/:session_id/element/:id/attribute/:name'],
76
78
  get_element_property: [:get, 'session/:session_id/element/:id/property/:name'],
77
79
  get_element_css_value: [:get, 'session/:session_id/element/:id/css/:property_name'],
80
+ get_element_aria_role: [:get, 'session/:session_id/element/:id/computedrole'],
81
+ get_element_aria_label: [:get, 'session/:session_id/element/:id/computedlabel'],
78
82
  get_element_text: [:get, 'session/:session_id/element/:id/text'],
79
83
  get_element_tag_name: [:get, 'session/:session_id/element/:id/name'],
80
84
  get_element_rect: [:get, 'session/:session_id/element/:id/rect'],
@@ -171,8 +175,6 @@ module Appium
171
175
  ime_deactivate: [:post, 'session/:session_id/ime/deactivate'],
172
176
  ime_activate_engine: [:post, 'session/:session_id/ime/activate'],
173
177
 
174
- send_keys_to_active_element: [:post, 'session/:session_id/keys'],
175
-
176
178
  ### Logs
177
179
  get_available_log_types: [:get, 'session/:session_id/log/types'],
178
180
  get_log: [:post, 'session/:session_id/log'],
@@ -18,10 +18,16 @@ module Appium
18
18
  module Device
19
19
  module AppManagement
20
20
  def launch_app
21
+ ::Appium::Logger.warn(
22
+ '[DEPRECATION] launch_app is deprecated. Please use activate_app instead.'
23
+ )
21
24
  execute :launch_app
22
25
  end
23
26
 
24
27
  def close_app
28
+ ::Appium::Logger.warn(
29
+ '[DEPRECATION] close_app is deprecated. Please use terminate_app instead.'
30
+ )
25
31
  execute :close_app
26
32
  end
27
33
 
@@ -39,21 +45,9 @@ module Appium
39
45
  raise NotImplementedError
40
46
  end
41
47
 
42
- def install_app(path,
43
- replace: nil,
44
- timeout: nil,
45
- allow_test_packages: nil,
46
- use_sdcard: nil,
47
- grant_permissions: nil)
48
+ def install_app(path, options = {})
48
49
  args = { appPath: path }
49
-
50
- args[:options] = {} if options?(replace, timeout, allow_test_packages, use_sdcard, grant_permissions)
51
-
52
- args[:options][:replace] = replace unless replace.nil?
53
- args[:options][:timeout] = timeout unless timeout.nil?
54
- args[:options][:allowTestPackages] = allow_test_packages unless allow_test_packages.nil?
55
- args[:options][:useSdcard] = use_sdcard unless use_sdcard.nil?
56
- args[:options][:grantPermissions] = grant_permissions unless grant_permissions.nil?
50
+ args[:options] = options unless options.empty?
57
51
 
58
52
  execute :install_app, {}, args
59
53
  end
@@ -41,9 +41,7 @@ module Appium
41
41
  # Keep .split(//) for backward compatibility for now
42
42
  text = keys.join
43
43
 
44
- # FIXME: further work for W3C. Over appium 1.15.0 or later
45
- # { value: text.split(//), text: text }
46
- { value: text.chars }
44
+ { text: text }
47
45
  end
48
46
  end # module Value
49
47
  end # module Device
@@ -17,10 +17,6 @@ module Appium
17
17
  module Error
18
18
  class CoreError < StandardError; end
19
19
 
20
- # Capability related errors
21
- class NoCapabilityError < CoreError; end
22
- class CapabilityStructureError < CoreError; end
23
-
24
20
  # Appium related errors
25
21
  class NotSupportedAppiumServer < CoreError; end
26
22
  class NoSuchElementError < CoreError; end