appium_lib_core 5.0.0 → 6.3.0

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