appium_lib 9.7.5 → 9.8.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/appium_lib.gemspec +1 -3
  4. data/contributing.md +2 -0
  5. data/docs/android_docs.md +186 -180
  6. data/docs/ios_docs.md +238 -234
  7. data/docs/ios_xcuitest.md +1 -1
  8. data/lib/appium_lib/android/common/helper.rb +0 -30
  9. data/lib/appium_lib/appium.rb +3 -4
  10. data/lib/appium_lib/common/helper.rb +0 -1
  11. data/lib/appium_lib/common/http_client.rb +1 -1
  12. data/lib/appium_lib/common/touch_actions.rb +4 -4
  13. data/lib/appium_lib/common/wait.rb +1 -1
  14. data/lib/appium_lib/driver.rb +7 -8
  15. data/lib/appium_lib/ios/common/helper.rb +1 -8
  16. data/lib/appium_lib/version.rb +2 -2
  17. data/readme.md +29 -8
  18. data/release_notes.md +6 -0
  19. metadata +5 -77
  20. data/lib/appium_lib/core/android.rb +0 -5
  21. data/lib/appium_lib/core/android/device.rb +0 -142
  22. data/lib/appium_lib/core/android/espresso/bridge.rb +0 -18
  23. data/lib/appium_lib/core/android/search_context.rb +0 -17
  24. data/lib/appium_lib/core/android/touch.rb +0 -15
  25. data/lib/appium_lib/core/android/uiautomator1/bridge.rb +0 -18
  26. data/lib/appium_lib/core/android/uiautomator2/bridge.rb +0 -18
  27. data/lib/appium_lib/core/android_espresso.rb +0 -5
  28. data/lib/appium_lib/core/android_uiautomator2.rb +0 -5
  29. data/lib/appium_lib/core/common.rb +0 -6
  30. data/lib/appium_lib/core/common/base.rb +0 -8
  31. data/lib/appium_lib/core/common/base/bridge.rb +0 -47
  32. data/lib/appium_lib/core/common/base/capabilities.rb +0 -16
  33. data/lib/appium_lib/core/common/base/command.rb +0 -10
  34. data/lib/appium_lib/core/common/base/driver.rb +0 -40
  35. data/lib/appium_lib/core/common/base/http_default.rb +0 -12
  36. data/lib/appium_lib/core/common/base/search_context.rb +0 -89
  37. data/lib/appium_lib/core/common/base/wait.rb +0 -56
  38. data/lib/appium_lib/core/common/command.rb +0 -75
  39. data/lib/appium_lib/core/common/device.rb +0 -462
  40. data/lib/appium_lib/core/common/error.rb +0 -18
  41. data/lib/appium_lib/core/common/log.rb +0 -30
  42. data/lib/appium_lib/core/common/logger.rb +0 -35
  43. data/lib/appium_lib/core/core.rb +0 -67
  44. data/lib/appium_lib/core/device/multi_touch.rb +0 -48
  45. data/lib/appium_lib/core/device/touch_actions.rb +0 -191
  46. data/lib/appium_lib/core/driver.rb +0 -417
  47. data/lib/appium_lib/core/ios.rb +0 -7
  48. data/lib/appium_lib/core/ios/device.rb +0 -44
  49. data/lib/appium_lib/core/ios/search_context.rb +0 -27
  50. data/lib/appium_lib/core/ios/touch.rb +0 -16
  51. data/lib/appium_lib/core/ios/uiautomation/bridge.rb +0 -20
  52. data/lib/appium_lib/core/ios/uiautomation/patch.rb +0 -20
  53. data/lib/appium_lib/core/ios/xcuitest/bridge.rb +0 -20
  54. data/lib/appium_lib/core/ios/xcuitest/device.rb +0 -59
  55. data/lib/appium_lib/core/ios/xcuitest/search_context.rb +0 -40
  56. data/lib/appium_lib/core/ios_xcuitest.rb +0 -8
  57. data/lib/appium_lib/core/patch.rb +0 -56
@@ -1,18 +0,0 @@
1
- module Appium
2
- module Core
3
- module Error
4
- class CoreError < StandardError; end
5
-
6
- # Capability related errors
7
- class NoCapabilityError < CoreError; end
8
- class CapabilityStructureError < CoreError; end
9
-
10
- # Appium related errors
11
- class NotSupportedAppiumServer < CoreError; end
12
- class NoSuchElementError < CoreError; end
13
-
14
- # Server side error
15
- class ServerError; end
16
- end
17
- end
18
- end
@@ -1,30 +0,0 @@
1
- module Appium
2
- module Core
3
- class Logs
4
- def initialize(bridge)
5
- @bridge = bridge
6
- end
7
-
8
- # @param [String|Hash] type You can get particular type's logs.
9
- # @return [[Selenium::WebDriver::LogEntry]] A list of logs data.
10
- #
11
- # @example
12
- # Appium::Core::Logs.new(driver).get("syslog") #=> [[Selenium::WebDriver::LogEntry]]
13
- # Appium::Core::Logs.new(driver).get(:syslog) #=> [[Selenium::WebDriver::LogEntry]]
14
- #
15
- def get(type)
16
- @bridge.get type
17
- end
18
-
19
- # Get a list of available log types
20
- #
21
- # @return [[Hash]] A list of available log types.
22
- # @example
23
- # Appium::Core::Logs.new(driver).available_types #=> [:syslog, :crashlog, :performance]
24
- #
25
- def available_types
26
- @bridge.available_types
27
- end
28
- end
29
- end # module Core
30
- end # module Appium
@@ -1,35 +0,0 @@
1
- require 'forwardable'
2
- require 'logger'
3
- require 'ap'
4
-
5
- module Appium
6
- module Logger
7
- #
8
- # @example Use logger manually
9
- # Appium::Logger.debug('This is info message')
10
- # Appium::Logger.warn('This is warning message')
11
- #
12
- class << self
13
- extend Forwardable
14
- def_delegators :logger, :ap, :fatal, :error, :warn, :info, :debug, :level, :level=, :formatter, :formatter=
15
-
16
- [:fatal, :error, :warn, :info, :debug].each do |level|
17
- define_method("ap_#{level}") { |obj| logger.ap(obj, level) }
18
- end
19
-
20
- attr_writer :logger
21
-
22
- private
23
-
24
- def logger
25
- @logger ||= begin
26
- logger = ::Logger.new($stdout)
27
- logger.progname = 'ruby_lib'
28
- logger.level = ::Logger::WARN
29
- logger.formatter = proc { |_severity, _datetime, _progname, msg| "#{msg}\n" } # do no special formatting
30
- logger
31
- end
32
- end
33
- end # class << self
34
- end # module Logger
35
- end # module Appium
@@ -1,67 +0,0 @@
1
- require 'selenium-webdriver'
2
-
3
- require_relative 'common'
4
- require_relative 'patch'
5
- require_relative 'driver'
6
-
7
- # for multi touch related methods
8
- require_relative 'device/touch_actions'
9
- require_relative 'device/multi_touch'
10
-
11
- require_relative 'android'
12
- require_relative 'android_uiautomator2'
13
- require_relative 'android_espresso'
14
-
15
- require_relative 'ios'
16
- require_relative 'ios_xcuitest'
17
-
18
- module Appium
19
- # convert all keys (including nested) to symbols
20
- #
21
- # based on deep_symbolize_keys & deep_transform_keys from rails
22
- # https://github.com/rails/docrails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/activesupport/lib/active_support/core_ext/hash/keys.rb#L84
23
- # @param [Hash] hash Hash value to make symbolise
24
- def self.symbolize_keys(hash)
25
- raise 'symbolize_keys requires a hash' unless hash.is_a? Hash
26
- result = {}
27
- hash.each do |key, value|
28
- key = key.to_sym rescue key # rubocop:disable Style/RescueModifier
29
- result[key] = value.is_a?(Hash) ? symbolize_keys(value) : value
30
- end
31
- result
32
- end
33
-
34
- module Core
35
- # Creates a new global driver and extend particular methods to `target`
36
- # @param [Class] target Extend particular methods to this target.
37
- # @param [Hash] opts A options include capabilities for the Appium Server and for the client.
38
- #
39
- # @example
40
- #
41
- # require 'rubygems'
42
- # require 'appium_lib'
43
- #
44
- # # Start iOS driver
45
- # opts = {
46
- # caps: {
47
- # platformName: :ios,
48
- # app: '/path/to/MyiOS.app'
49
- # },
50
- # appium_lib: {
51
- # server_url: "http://custom-host:8080/wd/hub.com",
52
- # export_session: false,
53
- # port: 8080,
54
- # wait: 0,
55
- # wait_timeout: 20,
56
- # wait_interval: 0.3,
57
- # listener: nil,
58
- # }
59
- # }
60
- # @core_driver = Appium::Core.for(self, opts) # create a core driver with `opts` and extend methods into `self`
61
- # @core_driver.start_driver(server_url: server_url, http_client_ops: http_client_ops) # start driver
62
- #
63
- def self.for(*args)
64
- Core::Driver.for(*args)
65
- end
66
- end
67
- end
@@ -1,48 +0,0 @@
1
- module Appium
2
- module Core
3
- # MultiTouch actions allow for multiple touches to happen at the same time,
4
- # for instance, to simulate multiple finger swipes.
5
- #
6
- # Create a series of touch actions by themselves (without a `prepare()`), then
7
- # add to a new MultiTouch action. When ready, call `prepare()` and all
8
- # actions will be executed simultaneously.
9
- #
10
- # @example
11
- #
12
- # action_1 = TouchAction.new.press(x: 45, y: 100).wait(5).release
13
- # action_2 = TouchAction.new.tap(element: el, x: 50, y:5, count: 3)
14
- #
15
- # multi_touch_action = MultiTouch.new
16
- # multi_touch_action.add action_1
17
- # multi_touch_action.add action_2
18
- # multi_touch_action.perform
19
- #
20
- # # with an arbitrary driver
21
- # driver = Appium::Driver.new(opts, false).start_driver
22
- # multi_touch_action = MultiTouch.new(driver)
23
- # multi_touch_action.add action_1
24
- # multi_touch_action.add action_2
25
- # multi_touch_action.perform
26
- #
27
- class MultiTouch
28
- attr_reader :driver
29
-
30
- def initialize(driver)
31
- @actions = []
32
- @driver = driver
33
- end
34
-
35
- # Add a touch_action to be performed
36
- # @param chain (TouchAction) The action to add to the chain
37
- def add(chain)
38
- @actions << chain.actions
39
- end
40
-
41
- # Ask Appium to perform the actions
42
- def perform
43
- @driver.multi_touch @actions
44
- @actions.clear
45
- end
46
- end # class MultiTouch
47
- end # module Core
48
- end # module Appium
@@ -1,191 +0,0 @@
1
- module Appium
2
- # Perform a series of gestures, one after another. Gestures are chained
3
- # together and only performed when `perform()` is called. Default is conducted by global driver.
4
- #
5
- # Each method returns the object itself, so calls can be chained.
6
- #
7
- # ```ruby
8
- # action = TouchAction(@driver).new.press(x: 45, y: 100).wait(5).release
9
- # action.perform
10
- # action = TouchAction.new.swipe(....)
11
- # action.perform
12
- # ```
13
- module Core
14
- class TouchAction
15
- ACTIONS = [:move_to, :long_press, :double_tap, :two_finger_tap, :press, :release, :tap, :wait, :perform].freeze
16
- COMPLEX_ACTIONS = [:swipe].freeze
17
-
18
- attr_reader :actions, :driver
19
-
20
- def initialize(driver)
21
- @actions = []
22
- @driver = driver
23
- end
24
-
25
- # Move to the given co-ordinates.
26
- #
27
- # `move_to`'s `x` and `y` have two case. One is working as coordinate, the other is working as offset.
28
- #
29
- # @option opts [integer] :x x co-ordinate to move to if element isn't set. Works as an offset if x is set with Element.
30
- # @option opts [integer] :y y co-ordinate to move to if element isn't set. Works as an offset if y is set with Element.
31
- # @option opts [WebDriver::Element] Element to scope this move within.
32
- def move_to(opts)
33
- opts = args_with_ele_ref(opts)
34
- chain_method(:moveTo, opts)
35
- end
36
-
37
- # Press down for a specific duration.
38
- # Alternatively, you can use `press(...).wait(...).release()` instead of `long_press` if duration doesn't work well.
39
- # https://github.com/appium/ruby_lib/issues/231#issuecomment-269895512
40
- # e.g. Appium::TouchAction.new.press(x: 280, y: 530).wait(2000).release.perform
41
- #
42
- # @option element [WebDriver::Element] the element to press.
43
- # @option x [integer] x co-ordinate to press on.
44
- # @option y [integer] y co-ordinate to press on.
45
- # @option duration [integer] Number of milliseconds to press.
46
- def long_press(opts)
47
- args = opts.select { |k, _v| [:element, :x, :y, :duration].include? k }
48
- args = args_with_ele_ref(args)
49
- chain_method(:longPress, args) # longPress is what the appium server expects
50
- end
51
-
52
- # Press a finger onto the screen. Finger will stay down until you call
53
- # `release`.
54
- #
55
- # @option opts [WebDriver::Element] :element (Optional) Element to press within.
56
- # @option opts [integer] :x x co-ordinate to press on
57
- # @option opts [integer] :y y co-ordinate to press on
58
- def press(opts)
59
- args = opts.select { |k, _v| [:element, :x, :y].include? k }
60
- args = args_with_ele_ref(args)
61
- chain_method(:press, args)
62
- end
63
-
64
- # Remove a finger from the screen.
65
- #
66
- # @option opts [WebDriver::Element] :element (Optional) Element to release from.
67
- # @option opts [integer] :x x co-ordinate to release from
68
- # @option opts [integer] :y y co-ordinate to release from
69
- def release(opts = nil)
70
- args = args_with_ele_ref(opts) if opts
71
- chain_method(:release, args)
72
- end
73
-
74
- # Touch a point on the screen.
75
- # Alternatively, you can use `press(...).release.perform` instead of `tap(...).perform`.
76
- #
77
- # @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
78
- # @option opts [integer] :x x co-ordinate to tap
79
- # @option opts [integer] :y y co-ordinate to tap
80
- # @option opts [integer] :fingers how many fingers to tap with (Default 1)
81
- def tap(opts)
82
- opts[:count] = opts.delete(:fingers) if opts[:fingers]
83
- opts[:count] ||= 1
84
- args = args_with_ele_ref opts
85
- chain_method(:tap, args)
86
- end
87
-
88
- # Double tap an element on the screen
89
- #
90
- # @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
91
- # @option opts [integer] :x x co-ordinate to tap
92
- # @option opts [integer] :y y co-ordinate to tap
93
-
94
- def double_tap(opts)
95
- args = opts.select { |k, _v| [:element, :x, :y].include? k }
96
- args = args_with_ele_ref(args)
97
- chain_method(:doubleTap, args) # doubleTap is what the appium server expects
98
- end
99
-
100
- # Two finger tap an element on the screen
101
- #
102
- # @option opts [WebDriver::Element] :element (Optional) Element to restrict scope too.
103
- # @option opts [integer] :x x co-ordinate to tap
104
- # @option opts [integer] :y y co-ordinate to tap
105
- def two_finger_tap(opts)
106
- args = opts.select { |k, _v| [:element, :x, :y].include? k }
107
- args = args_with_ele_ref(args)
108
- chain_method(:twoFingerTap, args) # twoFingerTap is what the appium server expects
109
- end
110
-
111
- # Pause for a number of milliseconds before the next action
112
- # @param milliseconds [integer] Number of milliseconds to pause for
113
- def wait(milliseconds)
114
- args = { ms: milliseconds }
115
- chain_method(:wait, args)
116
- end
117
-
118
- # Convenience method to peform a swipe.
119
- #
120
- # Note that iOS 7 simulators have broken swipe.
121
- #
122
- # For iOS: Use `offset_x` and `offset_y` to define the end point.
123
- #
124
- # For Android: Use `end_x` and `end_y` to define the end point.
125
- #
126
- # If you'd like more details, please read tests and its log samples in
127
- # `ios_tests/lib/ios/specs/device/touch_actions.rb` and `ios_tests/lib/ios/specs/device/touch_actions.rb`
128
- #
129
- # @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
130
- # @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
131
- # @option opts [int] :offset_x Offset, on the x axis. Default 0.
132
- # @option opts [int] :offset_y Offset, on the y axis. Default 0.
133
- # @option opts [int] :duration How long the actual swipe takes to complete in milliseconds. Default 200.
134
- def swipe(opts, ele = nil)
135
- start_x = opts.fetch :start_x, 0
136
- start_y = opts.fetch :start_y, 0
137
- offset_x = opts.fetch :offset_x, 0
138
- offset_y = opts.fetch :offset_y, 0
139
- duration = opts.fetch :duration, 200
140
-
141
- coordinates = swipe_coordinates(start_x: start_x, start_y: start_y, offset_x: offset_x, offset_y: offset_y)
142
-
143
- if ele # pinch/zoom for XCUITest
144
- press x: start_x, y: start_y, element: ele
145
- move_to x: coordinates[:offset_x], y: coordinates[:offset_y], element: ele
146
- else
147
- press x: start_x, y: start_y
148
- wait(duration) if duration
149
- move_to x: coordinates[:offset_x], y: coordinates[:offset_y]
150
- end
151
- release
152
-
153
- self
154
- end
155
-
156
- # Ask the driver to perform all actions in this action chain.
157
- def perform
158
- @driver.touch_actions @actions
159
- @actions.clear
160
- self
161
- end
162
-
163
- # Does nothing, currently.
164
- def cancel
165
- @actions << { action: cancel }
166
- @driver.touch_actions @actions
167
- self
168
- end
169
-
170
- # Visible for testing
171
- # @private
172
- def swipe_coordinates(start_x: 0, start_y: 0, offset_x: 0, offset_y: 0)
173
- raise NotImplementedError,
174
- "Please define swipe_coordinates with #{start_x}, #{start_y}, #{offset_x}, #{offset_y}"
175
- end
176
-
177
- private
178
-
179
- def chain_method(method, args = nil)
180
- action = args ? { action: method, options: args } : { action: method }
181
- @actions << action
182
- self
183
- end
184
-
185
- def args_with_ele_ref(args)
186
- args[:element] = args[:element].ref if args.key? :element
187
- args
188
- end
189
- end # class TouchAction
190
- end # module Core
191
- end # module Appium
@@ -1,417 +0,0 @@
1
- module Appium
2
- module Core
3
- class Driver
4
- # Selenium webdriver capabilities
5
- # @return [Core::Base::Capabilities]
6
- attr_reader :caps
7
-
8
- # Return http client called in start_driver()
9
- # @return [Appium::Core::Base::Http::Default] the http client
10
- attr_reader :http_client
11
-
12
- # Device type to request from the appium server
13
- # @return [Symbol] :android and :ios, for example
14
- attr_reader :device
15
-
16
- # Automation name sent to appium server or received from server
17
- # If automation_name is nil, it is not set both client side and server side.
18
- # @return [Hash]
19
- attr_reader :automation_name
20
-
21
- # Custom URL for the selenium server. If set this attribute, ruby_lib try to handshake to the custom url.
22
- # @return [String]
23
- attr_reader :custom_url
24
-
25
- # Export session id to textfile in /tmp for 3rd party tools
26
- # @return [Boolean]
27
- attr_reader :export_session
28
- # @return [String] By default, session id is exported in '/tmp/appium_lib_session'
29
- attr_reader :export_session_path
30
-
31
- # Default wait time for elements to appear
32
- # Returns the default client side wait.
33
- # This value is independent of what the server is using
34
- # Provide Appium::Drive like { appium_lib: { wait: 20 } }
35
- # @return [Integer]
36
- attr_reader :default_wait
37
-
38
- # Appium's server port
39
- # Provide Appium::Drive like { appium_lib: { port: 8080 } }
40
- # @return [Integer]
41
- attr_reader :port
42
-
43
- # Return a time wait timeout
44
- # Wait time for ::Appium::Core::Base::Wait, wait and wait_true
45
- # Provide Appium::Drive like { appium_lib: { wait_timeout: 20 } }
46
- # @return [Integer]
47
- attr_reader :wait_timeout
48
-
49
- # Return a time wait timeout
50
- # Wait interval time for ::Appium::Core::Base::Wait, wait and wait_true
51
- # Provide Appium::Drive like { appium_lib: { wait_interval: 20 } }
52
- # @return [Integer]
53
- attr_reader :wait_interval
54
-
55
- # instance of AbstractEventListener for logging support
56
- attr_reader :listener
57
-
58
- # @return [Appium::Core::Base::Driver]
59
- attr_reader :driver
60
-
61
- # @private
62
- # @see Appium::Core.for
63
- #
64
- # @return [Driver]
65
- #
66
- def self.for(target, opts = {})
67
- new(target, opts)
68
- end
69
-
70
- # @private
71
- def initialize(target, opts = {})
72
- opts = Appium.symbolize_keys opts
73
- validate_keys(opts)
74
-
75
- @caps = get_caps(opts)
76
-
77
- set_appium_lib_specific_values(get_appium_lib_opts(opts))
78
- set_app_path
79
- set_appium_device
80
- set_automation_name
81
-
82
- extend_for(device: @device, automation_name: @automation_name, target: target)
83
-
84
- self
85
- end
86
-
87
- # Creates a new global driver and quits the old one if it exists.
88
- # You can customise http_client as the following
89
- #
90
- # @param [String] server_url Custom server url to send to requests. Default is "http://127.0.0.1:4723/wd/hub".
91
- # @option http_client_ops [Hash] :http_client Custom HTTP Client
92
- # @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
93
- # @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
94
- # @return [Selenium::WebDriver] the new global driver
95
- #
96
- # @example
97
- #
98
- # require 'rubygems'
99
- # require 'appium_lib'
100
- #
101
- # # platformName takes a string or a symbol.
102
- #
103
- # # Start iOS driver
104
- # opts = {
105
- # caps: {
106
- # platformName: :ios,
107
- # app: '/path/to/MyiOS.app'
108
- # },
109
- # appium_lib: {
110
- # server_url: "http://custom-host:8080/wd/hub.com",
111
- # export_session: false,
112
- # port: 8080,
113
- # wait: 0,
114
- # wait_timeout: 20,
115
- # wait_interval: 0.3,
116
- # listener: nil,
117
- # }
118
- # }
119
- # @core = Appium::Driver.new(opts)
120
- # @driver = @core.start_driver
121
- #
122
- def start_driver(server_url: nil,
123
- http_client_ops: { http_client: nil, open_timeout: 999_999, read_timeout: 999_999 })
124
- server_url = server_url ? server_url : "http://127.0.0.1:#{@port}/wd/hub"
125
-
126
- # open_timeout and read_timeout are explicit wait.
127
- open_timeout = http_client_ops.delete(:open_timeout)
128
- read_timeout = http_client_ops.delete(:read_timeout)
129
-
130
- http_client = http_client_ops.delete(:http_client)
131
- @http_client ||= http_client ? http_client : Appium::Core::Base::Http::Default.new
132
-
133
- @http_client.open_timeout = open_timeout if open_timeout
134
- @http_client.read_timeout = read_timeout if read_timeout
135
-
136
- begin
137
- # included https://github.com/SeleniumHQ/selenium/blob/43f8b3f66e7e01124eff6a5805269ee441f65707/rb/lib/selenium/webdriver/remote/driver.rb#L29
138
- @driver = ::Appium::Core::Base::Driver.new(http_client: @http_client,
139
- desired_capabilities: @caps,
140
- url: server_url,
141
- listener: @listener)
142
-
143
- # export session
144
- write_session_id(@driver.session_id, @export_session_path) if @export_session
145
- rescue Errno::ECONNREFUSED
146
- raise "ERROR: Unable to connect to Appium. Is the server running on #{server_url}?"
147
- end
148
-
149
- # If "automationName" is set only server side, this method set "automationName" attribute into @automation_name.
150
- # Since @automation_name is set only client side before start_driver is called.
151
- set_automation_name_if_nil
152
-
153
- @driver
154
- end
155
-
156
- # Quits the driver
157
- # @return [void]
158
- #
159
- # @example
160
- #
161
- # @core.quit_driver
162
- #
163
- def quit_driver
164
- @driver.quit
165
- rescue
166
- nil
167
- end
168
-
169
- # Returns the server's version info
170
- # @return [Hash]
171
- #
172
- # @example
173
- #
174
- # @core.appium_server_version
175
- # {
176
- # "build" => {
177
- # "version" => "0.18.1",
178
- # "revision" => "d242ebcfd92046a974347ccc3a28f0e898595198"
179
- # }
180
- # }
181
- #
182
- # Returns blank hash for Selenium Grid since `remote_status` gets 500 error
183
- #
184
- # @example
185
- #
186
- # @core.appium_server_version #=> {}
187
- #
188
- def appium_server_version
189
- @driver.remote_status
190
- rescue Selenium::WebDriver::Error::ServerError => e
191
- raise ::Appium::Core::Error::ServerError unless e.message.include?('status code 500')
192
- # driver.remote_status returns 500 error for using selenium grid
193
- {}
194
- end
195
-
196
- # Return the platform version as an array of integers
197
- # @return [Array<Integer>]
198
- #
199
- # @example
200
- #
201
- # @core.platform_version #=> [10.1.1]
202
- #
203
- def platform_version
204
- p_version = @driver.capabilities['platformVersion']
205
- p_version.split('.').map(&:to_i)
206
- end
207
-
208
- # Takes a png screenshot and saves to the target path.
209
- #
210
- # @param png_save_path [String] the full path to save the png
211
- # @return [File]
212
- #
213
- # @example
214
- #
215
- # @core.screenshot '/tmp/hi.png' #=> nil
216
- # # same as `@driver.save_screenshot png_save_path`
217
- #
218
- def screenshot(png_save_path)
219
- warn '[DEPRECATION] screenshot will be removed. Please use driver.save_screenshot instead.'
220
- @driver.save_screenshot png_save_path
221
- end
222
-
223
- # Check every interval seconds to see if yield returns a truthy value.
224
- # Note this isn't a strict boolean true, any truthy value is accepted.
225
- # false and nil are considered failures.
226
- # Give up after timeout seconds.
227
- #
228
- # Wait code from the selenium Ruby gem
229
- # https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
230
- #
231
- # If only a number is provided then it's treated as the timeout value.
232
- #
233
- # @param [Hash] opts Options
234
- # @option opts [Numeric] :timeout Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
235
- # @option opts [Numeric] :interval Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
236
- # @option opts [String] :message Exception message if timed out.
237
- # @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
238
- #
239
- # @example
240
- #
241
- # @core.wait_true { @driver.find_element :accessibility_id, 'something' }
242
- #
243
- def wait_true(opts = {})
244
- opts = process_wait_opts(opts).merge(return_if_true: true)
245
-
246
- opts[:timeout] ||= @wait_timeout
247
- opts[:interval] ||= @wait_interval
248
-
249
- wait = ::Appium::Core::Base::Wait.new opts
250
- wait.until { yield }
251
- end
252
-
253
- # Check every interval seconds to see if yield doesn't raise an exception.
254
- # Give up after timeout seconds.
255
- #
256
- # Wait code from the selenium Ruby gem
257
- # https://github.com/SeleniumHQ/selenium/blob/cf501dda3f0ed12233de51ce8170c0e8090f0c20/rb/lib/selenium/webdriver/common/wait.rb
258
- #
259
- # If only a number is provided then it's treated as the timeout value.
260
- #
261
- # @param [Hash] opts Options
262
- # @option opts [Numeric] :timeout Seconds to wait before timing out. Set default by `appium_wait_timeout` (30).
263
- # @option opts [Numeric] :interval Seconds to sleep between polls. Set default by `appium_wait_interval` (0.5).
264
- # @option opts [String] :message Exception message if timed out.
265
- # @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Exception)
266
- #
267
- # @example
268
- #
269
- # @core.wait_true { @driver.find_element :accessibility_id, 'something' }
270
- #
271
- def wait(opts = {})
272
- opts = process_wait_opts(opts).merge(return_if_true: false)
273
-
274
- opts[:timeout] ||= @wait_timeout
275
- opts[:interval] ||= @wait_interval
276
-
277
- wait = ::Appium::Core::Base::Wait.new opts
278
- wait.until { yield }
279
- end
280
-
281
- private
282
-
283
- # @private
284
- def process_wait_opts(opts)
285
- opts = { timeout: opts } if opts.is_a?(Numeric)
286
- raise 'opts must be a hash' unless opts.is_a? Hash
287
- opts
288
- end
289
-
290
- # @private
291
- def extend_for(device:, automation_name:, target:)
292
- target.extend Appium::Core
293
- target.extend Appium::Core::Device
294
-
295
- case device
296
- when :android
297
- case automation_name
298
- when :espresso
299
- ::Appium::Core::Android::Espresso::Bridge.for(self)
300
- when :uiautomator2
301
- ::Appium::Core::Android::Uiautomator2::Bridge.for(self)
302
- else # default and UiAutomator
303
- ::Appium::Core::Android::Uiautomator1::Bridge.for(self)
304
- end
305
- when :ios
306
- case automation_name
307
- when :xcuitest
308
- ::Appium::Core::Ios::Xcuitest::Bridge.for(self)
309
- else # default and UIAutomation
310
- ::Appium::Core::Ios::Uiautomation::Bridge.for(self)
311
- end
312
- when :mac
313
- # no Mac specific extentions
314
- Appium::Logger.debug('mac')
315
- when :windows
316
- # no windows specific extentions
317
- Appium::Logger.debug('windows')
318
- else
319
- Appium::Logger.warn('no device matched')
320
- end
321
-
322
- target
323
- end
324
-
325
- # @private
326
- def validate_keys(opts)
327
- flatten_ops = flatten_hash_keys(opts)
328
-
329
- raise Error::NoCapabilityError unless opts.member?(:caps)
330
- if !opts.member?(:appium_lib) && flatten_ops.member?(:appium_lib)
331
- raise Error::CapabilityStructureError, 'Please check the value of appium_lib in the capability'
332
- end
333
-
334
- true
335
- end
336
-
337
- # @private
338
- def flatten_hash_keys(hash, flatten_keys_result = [])
339
- hash.each do |key, value|
340
- flatten_keys_result << key
341
- flatten_hash_keys(value, flatten_keys_result) if value.is_a?(Hash)
342
- end
343
-
344
- flatten_keys_result
345
- end
346
-
347
- # @private
348
- def get_caps(opts)
349
- Core::Base::Capabilities.create_capabilities(opts[:caps] || {})
350
- end
351
-
352
- # @private
353
- def get_appium_lib_opts(opts)
354
- opts[:appium_lib] || {}
355
- end
356
-
357
- # @private
358
- # Path to the .apk, .app or .app.zip.
359
- # The path can be local or remote for Sauce.
360
- def set_app_path
361
- return unless @caps && @caps[:app] && !@caps[:app].empty?
362
- @caps[:app] = File.expand_path(@caps[:app])
363
- end
364
-
365
- # @private
366
- def set_appium_lib_specific_values(appium_lib_opts)
367
- @custom_url = appium_lib_opts.fetch :server_url, false
368
- @default_wait = appium_lib_opts.fetch :wait, 0
369
-
370
- # bump current session id into a particular file
371
- @export_session = appium_lib_opts.fetch :export_session, false
372
- @export_session_path = appium_lib_opts.fetch :export_session_path, '/tmp/appium_lib_session'
373
-
374
- @port = appium_lib_opts.fetch :port, 4723
375
-
376
- # timeout and interval used in ::Appium::Comm.wait/wait_true
377
- @wait_timeout = appium_lib_opts.fetch :wait_timeout, 30
378
- @wait_interval = appium_lib_opts.fetch :wait_interval, 0.5
379
-
380
- # to pass it in Selenium.new.
381
- # `listener = opts.delete(:listener)` is called in Selenium::Driver.new
382
- @listener = appium_lib_opts.fetch :listener, nil
383
- end
384
-
385
- # @private
386
- def set_appium_device
387
- # https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
388
- @device = @caps[:platformName]
389
- return @device unless @device
390
-
391
- @device = @device.is_a?(Symbol) ? @device : @device.downcase.strip.intern
392
- end
393
-
394
- # @private
395
- def set_automation_name
396
- @automation_name = @caps[:automationName] if @caps[:automationName]
397
- @automation_name = if @automation_name
398
- @automation_name.is_a?(Symbol) ? @automation_name : @automation_name.downcase.strip.intern
399
- end
400
- end
401
-
402
- # @private
403
- def set_automation_name_if_nil
404
- return unless @automation_name.nil?
405
- @automation_name = @driver.capabilities['automationName']
406
- end
407
-
408
- # @private
409
- def write_session_id(session_id, export_path = '/tmp/appium_lib_session')
410
- File.open(export_path, 'w') { |f| f.puts session_id }
411
- rescue IOError => e
412
- ::Appium::Logger.warn e
413
- nil
414
- end
415
- end # class Driver
416
- end # module Core
417
- end # module Appium