appium_lib 9.7.1 → 9.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -75,6 +75,7 @@ module Appium
75
75
 
76
76
  data
77
77
  end
78
+ # @see load_settings
78
79
  alias load_appium_txt load_settings
79
80
 
80
81
  # @param [String] base_dir parent directory of loaded appium.txt (toml)
@@ -119,6 +120,9 @@ module Appium
119
120
  # if modules is a module instead of an array, then the constants of
120
121
  # that module are promoted on.
121
122
  # otherwise, the array of modules will be used as the promotion target.
123
+ #
124
+ # @param [Array<Module>] modules An array of modules
125
+ # @param [Driver] driver A driver to extend for
122
126
  def promote_singleton_appium_methods(modules, driver = $driver)
123
127
  raise 'Global $driver is nil' if driver.nil?
124
128
 
@@ -153,25 +157,27 @@ module Appium
153
157
  ##
154
158
  # Promote appium methods to class instance methods
155
159
  #
156
- # @param class_array [Array<Class>] An array of classes
160
+ # @param [Array<Class>] class_array An array of classes
161
+ # @param [Driver] driver A driver to extend for
157
162
  #
158
163
  # To promote methods to all classes:
159
164
  #
160
- # ```ruby
161
- # Appium.promote_appium_methods Object
162
- # ```
165
+ # @example
166
+ #
167
+ # Appium.promote_appium_methods Object
163
168
  #
164
169
  # It's better to promote on specific classes instead of Object
165
170
  #
166
- # ```ruby
167
- # # promote on rspec
168
- # Appium.promote_appium_methods RSpec::Core::ExampleGroup
169
- # ```
171
+ # @example
172
+ #
173
+ # # promote on rspec
174
+ # Appium.promote_appium_methods RSpec::Core::ExampleGroup
175
+ #
176
+ # @example
177
+ #
178
+ # # promote on minispec
179
+ # Appium.promote_appium_methods Minitest::Spec
170
180
  #
171
- # ```ruby
172
- # # promote on minispec
173
- # Appium.promote_appium_methods Minitest::Spec
174
- # ```
175
181
  def promote_appium_methods(class_array, driver = $driver)
176
182
  raise 'Driver is nil' if driver.nil?
177
183
  # Wrap single class into an array
@@ -198,5 +204,5 @@ module Appium
198
204
  end
199
205
  nil # return nil
200
206
  end
201
- end
202
- end
207
+ end # class << self
208
+ end # module Appium
@@ -1,5 +1,4 @@
1
- # Generic helper methods not specific
2
- # to a particular tag name
1
+ # Generic helper methods not specific to a particular tag name
3
2
  module Appium
4
3
  module Common
5
4
  # iOS .name returns the accessibility attribute if it's set. if not set, the string value is used.
@@ -26,6 +25,12 @@ module Appium
26
25
  end
27
26
 
28
27
  # For Sauce Labs reporting. Returns the current session id.
28
+ # @return [String]
29
+ #
30
+ # @example
31
+ #
32
+ # @driver.session_id #=> "some-session-ids"
33
+ #
29
34
  def session_id
30
35
  @driver.session_id
31
36
  end
@@ -81,6 +86,14 @@ module Appium
81
86
  end # class CountElements
82
87
 
83
88
  # Returns a string of class counts of visible elements.
89
+ # @return [String]
90
+ #
91
+ # @example
92
+ #
93
+ # get_page_class #=> "24x XCUIElementTypeStaticText\n12x XCUIElementTypeCell\n8x XCUIElementTypeOther\n
94
+ # # 2x XCUIElementTypeWindow\n1x XCUIElementTypeStatusBar\n1x XCUIElementTypeTable\n1
95
+ # # x XCUIElementTypeNavigationBar\n1x XCUIElementTypeApplication"
96
+ #
84
97
  def get_page_class
85
98
  parser = @count_elements_parser ||= Nokogiri::XML::SAX::Parser.new(CountElements.new)
86
99
 
@@ -92,6 +105,20 @@ module Appium
92
105
 
93
106
  # Count all classes on screen and print to stdout.
94
107
  # Useful for appium_console.
108
+ # @return [nil]
109
+ #
110
+ # @example
111
+ #
112
+ # page_class
113
+ # # 24x XCUIElementTypeStaticText
114
+ # # 12x XCUIElementTypeCell
115
+ # # 8x XCUIElementTypeOther
116
+ # # 2x XCUIElementTypeWindow
117
+ # # 1x XCUIElementTypeStatusBar
118
+ # # 1x XCUIElementTypeTable
119
+ # # 1x XCUIElementTypeNavigationBar
120
+ # # 1x XCUIElementTypeApplication
121
+ #
95
122
  def page_class
96
123
  puts get_page_class
97
124
  nil
@@ -99,9 +126,10 @@ module Appium
99
126
 
100
127
  # Converts pixel values to window relative values
101
128
  #
102
- # ```ruby
103
- # px_to_window_rel x: 50, y: 150
104
- # ```
129
+ # @example
130
+ #
131
+ # px_to_window_rel x: 50, y: 150 #=> #<OpenStruct x="50.0 / 375.0", y="150.0 / 667.0">
132
+ #
105
133
  def px_to_window_rel(opts = {}, driver = $driver)
106
134
  w = driver.window_size
107
135
  x = opts.fetch :x, 0
@@ -142,6 +170,7 @@ module Appium
142
170
  @strings_xml[id]
143
171
  end
144
172
 
173
+ # @private
145
174
  class HTMLElements < Nokogiri::XML::SAX::Document
146
175
  attr_reader :filter
147
176
 
@@ -4,6 +4,7 @@ require_relative '../version'
4
4
  module Appium
5
5
  module Http
6
6
  class Default < ::Appium::Core::Base::Http::Default
7
+ # Default HTTP client inherit Appium::Core::Base::Http::Default, but has different DEFAULT_HEADERS
7
8
  DEFAULT_HEADERS = { 'Accept' => CONTENT_TYPE, 'User-Agent' => "appium/ruby_lib/#{::Appium::VERSION}" }.freeze
8
9
  end
9
10
  end
@@ -4,6 +4,7 @@ module Appium
4
4
  # @return [[Selenium::WebDriver::LogEntry]] A list of logs data.
5
5
  #
6
6
  # @example
7
+ #
7
8
  # @driver.get_log("syslog") #=> [[Selenium::WebDriver::LogEntry]]
8
9
  # @driver.get_log(:syslog) #=> [[Selenium::WebDriver::LogEntry]]
9
10
  #
@@ -14,7 +15,9 @@ module Appium
14
15
  # Get a list of available log types
15
16
  #
16
17
  # @return [[String]] A list of available log types.
18
+ #
17
19
  # @example
20
+ #
18
21
  # @driver.get_available_log_types #=> [:syslog, :crashlog, :performance]
19
22
  #
20
23
  def get_available_log_types
@@ -6,25 +6,23 @@ module Appium
6
6
  # add to a new MultiTouch action. When ready, call `prepare()` and all
7
7
  # actions will be executed simultaneously.
8
8
  #
9
- # ```ruby
10
- # action_1 = TouchAction.new.press(x: 45, y: 100).wait(5).release
11
- # action_2 = TouchAction.new.tap(element: el, x: 50, y:5, count: 3)
9
+ # @example
12
10
  #
13
- # multi_touch_action = MultiTouch.new
14
- # multi_touch_action.add action_1
15
- # multi_touch_action.add action_2
16
- # multi_touch_action.perform
17
- # ```
11
+ # action_1 = TouchAction.new.press(x: 45, y: 100).wait(5).release
12
+ # action_2 = TouchAction.new.tap(element: el, x: 50, y:5, count: 3)
13
+ #
14
+ # multi_touch_action = MultiTouch.new
15
+ # multi_touch_action.add action_1
16
+ # multi_touch_action.add action_2
17
+ # multi_touch_action.perform
18
+ #
19
+ # # with an arbitrary driver
20
+ # driver = Appium::Driver.new(opts, false).start_driver
21
+ # multi_touch_action = MultiTouch.new(driver)
22
+ # multi_touch_action.add action_1
23
+ # multi_touch_action.add action_2
24
+ # multi_touch_action.perform
18
25
  #
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
-
28
26
  class MultiTouch < ::Appium::Core::MultiTouch
29
27
  class << self
30
28
  # Convenience method for pinching the screen.
@@ -33,23 +31,23 @@ module Appium
33
31
  # @param auto_perform (boolean) Whether to perform the action immediately (default true)
34
32
  # @param driver (Driver) Set a driver to conduct the action. DEfault is global driver($driver)
35
33
  #
36
- # ```ruby
37
- # pinch 75 #=> Pinch the screen from the top right and bottom left corners
38
- # ```
34
+ # @example
35
+ #
36
+ # pinch 75 #=> Pinch the screen from the top right and bottom left corners
39
37
  #
40
38
  # Without auto_perform
41
39
  #
42
- # ```ruby
43
- # action = pinch 75, false #=> Pinch the screen from the top right and bottom left corners
44
- # action.perform #=> to 25% of its size.
45
- # ```
40
+ # @example
41
+ #
42
+ # action = pinch 75, false #=> Pinch the screen from the top right and bottom left corners
43
+ # action.perform #=> to 25% of its size.
46
44
  #
47
45
  # With driver
48
46
  #
49
- # ```ruby
50
- # driver = Appium::Driver.new(opts, false).start_driver
51
- # pinch 75, true, driver #=> Pinch the screen from the top right and bottom left corners
52
- # ```
47
+ # @example
48
+ # driver = Appium::Driver.new(opts, false).start_driver
49
+ # pinch 75, true, driver #=> Pinch the screen from the top right and bottom left corners
50
+ #
53
51
  def pinch(percentage = 25, auto_perform = true, driver = $driver)
54
52
  ::Appium::Core::MultiTouch.pinch percentage: percentage, auto_perform: auto_perform, driver: driver
55
53
  end
@@ -60,23 +58,24 @@ module Appium
60
58
  # @param auto_perform (boolean) Whether to perform the action immediately (default true)
61
59
  # @param driver (Driver) Set a driver to conduct the action. DEfault is global driver($driver)
62
60
  #
63
- # ```ruby
64
- # action = zoom 200 #=> Zoom in the screen from the center until it doubles in size.
65
- # ```
61
+ # @example
62
+ #
63
+ # action = zoom 200 #=> Zoom in the screen from the center until it doubles in size.
66
64
  #
67
65
  # Without auto_perform
68
66
  #
69
- # ```ruby
70
- # action = zoom 200, false #=> Zoom in the screen from the center until it doubles in size.
71
- # action.perform #=> to 25% of its size.
72
- # ```
67
+ # @example
68
+ #
69
+ # action = zoom 200, false #=> Zoom in the screen from the center until it doubles in size.
70
+ # action.perform #=> to 25% of its size.
73
71
  #
74
72
  # With driver
75
73
  #
76
- # ```ruby
77
- # driver = Appium::Driver.new(opts, false).start_driver
78
- # pinch 200, true, driver #=> Zoom in the screen from the center until it doubles in size.
79
- # ```
74
+ # @example
75
+ #
76
+ # driver = Appium::Driver.new(opts, false).start_driver
77
+ # pinch 200, true, driver #=> Zoom in the screen from the center until it doubles in size.
78
+ #
80
79
  def zoom(percentage = 200, auto_perform = true, driver = $driver)
81
80
  ::Appium::Core::MultiTouch.zoom percentage: percentage, auto_perform: auto_perform, driver: driver
82
81
  end
@@ -4,28 +4,31 @@ module Appium
4
4
  #
5
5
  # Each method returns the object itself, so calls can be chained.
6
6
  #
7
- # ```ruby
8
- # action = TouchAction.new.press(x: 45, y: 100).wait(5).release
9
- # action.perform
10
- # action = TouchAction.new.swipe(....)
11
- # action.perform
12
- # ```
7
+ # @example
8
+ #
9
+ # action = TouchAction.new.press(x: 45, y: 100).wait(5).release
10
+ # action.perform
11
+ # action = TouchAction.new.swipe(....)
12
+ # action.perform
13
13
  #
14
14
  # Or each methods can call without `TouchAction.new` as the following.
15
15
  # In this case, `perform` is called automatically.
16
- # ```ruby
17
- # # called `swipe(...).perform` in this method.
18
- # swipe(start_x: 75, start_y: 500, offset_x: 75, offset_y: 20, duration: 500)
19
- # ```
16
+ #
17
+ # @example
18
+ #
19
+ # # called `swipe(...).perform` in this method.
20
+ # swipe(start_x: 75, start_y: 500, offset_x: 75, offset_y: 20, duration: 500)
20
21
  #
21
22
  # If you'd like to perform the chain with an arbitrary driver:
22
- # ```ruby
23
- # driver = Appium::Driver.new(opts, false).start_driver
24
- # action = TouchAction.new.press(x: 45, y: 100).wait(5).release
25
- # action.perform(@driver)
26
- # action = TouchAction.new.swipe(....)
27
- # action.perform(@driver)
28
- # ```
23
+ #
24
+ # @example
25
+ #
26
+ # driver = Appium::Driver.new(opts, false).start_driver
27
+ # action = TouchAction.new.press(x: 45, y: 100).wait(5).release
28
+ # action.perform(@driver)
29
+ # action = TouchAction.new.swipe(....)
30
+ # action.perform(@driver)
31
+ #
29
32
  class TouchAction < ::Appium::Core::TouchAction
30
33
  COMPLEX_ACTIONS = ::Appium::Core::TouchAction::COMPLEX_ACTIONS
31
34
 
@@ -3,6 +3,11 @@ module Appium
3
3
  module Error
4
4
  class CoreError < StandardError; end
5
5
 
6
+ # Capability related errors
7
+ class NoCapabilityError < CoreError; end
8
+ class CapabilityStructureError < CoreError; end
9
+
10
+ # Appium related errors
6
11
  class NotSupportedAppiumServer < CoreError; end
7
12
  class NoSuchElementError < CoreError; end
8
13
 
@@ -31,6 +31,34 @@ module Appium
31
31
  end
32
32
 
33
33
  module Core
34
+ # Creates a new global driver and extend particular methods to `target`
35
+ # @param [Class] target Extend particular methods to this target.
36
+ # @param [Hash] opts A options include capabilities for the Appium Server and for the client.
37
+ #
38
+ # @example
39
+ #
40
+ # require 'rubygems'
41
+ # require 'appium_lib'
42
+ #
43
+ # # Start iOS driver
44
+ # opts = {
45
+ # caps: {
46
+ # platformName: :ios,
47
+ # app: '/path/to/MyiOS.app'
48
+ # },
49
+ # appium_lib: {
50
+ # server_url: "http://custom-host:8080/wd/hub.com",
51
+ # export_session: false,
52
+ # port: 8080,
53
+ # wait: 0,
54
+ # wait_timeout: 20,
55
+ # wait_interval: 0.3,
56
+ # listener: nil,
57
+ # }
58
+ # }
59
+ # @core_driver = Appium::Core.for(self, opts) # create a core driver with `opts` and extend methods into `self`
60
+ # @core_driver.start_driver(server_url: server_url, http_client_ops: http_client_ops) # start driver
61
+ #
34
62
  def self.for(*args)
35
63
  Core::Driver.for(*args)
36
64
  end
@@ -2,36 +2,53 @@ module Appium
2
2
  module Core
3
3
  class Driver
4
4
  # Selenium webdriver capabilities
5
+ # @return [Core::Base::Capabilities]
5
6
  attr_reader :caps
6
- # Custom URL for the selenium server
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
+ attr_reader :device
14
+
15
+ # Automation name sent to appium server or received from server
16
+ # If automation_name is nil, it is not set both client side and server side.
17
+ # @return [Hash]
18
+ attr_reader :automation_name
19
+
20
+ # Custom URL for the selenium server. If set this attribute, ruby_lib try to handshake to the custom url.
21
+ # @return [String]
7
22
  attr_reader :custom_url
23
+
8
24
  # Export session id to textfile in /tmp for 3rd party tools
25
+ # @return [Boolean]
9
26
  attr_reader :export_session
27
+
10
28
  # Default wait time for elements to appear
11
29
  # Returns the default client side wait.
12
30
  # This value is independent of what the server is using
31
+ # Provide Appium::Drive like { appium_lib: { wait: 20 } }
13
32
  # @return [Integer]
14
33
  attr_reader :default_wait
34
+
15
35
  # Appium's server port
36
+ # Provide Appium::Drive like { appium_lib: { port: 8080 } }
37
+ # @return [Integer]
16
38
  attr_reader :port
17
- # Device type to request from the appium server
18
- attr_reader :device
19
- # Automation name sent to appium server or received from server
20
- # If automation_name is nil, it is not set both client side and server side.
21
- attr_reader :automation_name
39
+
22
40
  # Return a time wait timeout
23
41
  # Wait time for ::Appium::Common.wait or ::Appium::Common.wait_true.
24
42
  # Provide Appium::Drive like { appium_lib: { wait_timeout: 20 } }
25
43
  # @return [Integer]
26
44
  attr_reader :wait_timeout
45
+
27
46
  # Return a time wait timeout
28
47
  # Wait interval time for ::Appium::Common.wait or ::Appium::Common.wait_true.
29
48
  # Provide Appium::Drive like { appium_lib: { wait_interval: 20 } }
30
49
  # @return [Integer]
31
50
  attr_reader :wait_interval
32
- # Return http client called in start_driver()
33
- # @return [Appium::Core::Base::Http::Default] the http client
34
- attr_reader :http_client
51
+
35
52
  # instance of AbstractEventListener for logging support
36
53
  attr_reader :listener
37
54
 
@@ -54,6 +71,8 @@ module Appium
54
71
  # @private
55
72
  def initialize(target, opts = {})
56
73
  opts = Appium.symbolize_keys opts
74
+ validate_keys(opts)
75
+
57
76
  @caps = get_caps(opts)
58
77
 
59
78
  set_appium_lib_specific_values(get_appium_lib_opts(opts))
@@ -69,8 +88,14 @@ module Appium
69
88
  # Creates a new global driver and quits the old one if it exists.
70
89
  # You can customise http_client as the following
71
90
  #
91
+ # @param [String] server_url Custom server url to send to requests. Default is "http://127.0.0.1:4723/wd/hub".
92
+ # @option http_client_ops [Hash] :http_client Custom HTTP Client
93
+ # @option http_client_ops [Hash] :open_timeout Custom open timeout for http client.
94
+ # @option http_client_ops [Hash] :read_timeout Custom read timeout for http client.
95
+ # @return [Selenium::WebDriver] the new global driver
96
+ #
72
97
  # @example
73
- # ```ruby
98
+ #
74
99
  # require 'rubygems'
75
100
  # require 'appium_lib'
76
101
  #
@@ -83,17 +108,20 @@ module Appium
83
108
  # app: '/path/to/MyiOS.app'
84
109
  # },
85
110
  # appium_lib: {
86
- # wait_timeout: 30
111
+ # server_url: "http://custom-host:8080/wd/hub.com",
112
+ # export_session: false,
113
+ # port: 8080,
114
+ # wait: 0,
115
+ # wait_timeout: 20,
116
+ # wait_interval: 0.3,
117
+ # listener: nil,
87
118
  # }
88
119
  # }
89
- # Appium::Driver.new(opts).start_driver
120
+ # @driver = Appium::Driver.new(opts).start_driver
90
121
  #
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
- def start_driver(server_url:,
122
+ def start_driver(server_url: nil,
96
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"
97
125
 
98
126
  # open_timeout and read_timeout are explicit wait.
99
127
  open_timeout = http_client_ops.delete(:open_timeout)
@@ -134,23 +162,24 @@ module Appium
134
162
  end
135
163
 
136
164
  # Returns the server's version info
165
+ # @return [Hash]
137
166
  #
138
- # ```ruby
139
- # {
140
- # "build" => {
141
- # "version" => "0.18.1",
142
- # "revision" => "d242ebcfd92046a974347ccc3a28f0e898595198"
167
+ # @example
168
+ #
169
+ # @driver.appium_server_version
170
+ # {
171
+ # "build" => {
172
+ # "version" => "0.18.1",
173
+ # "revision" => "d242ebcfd92046a974347ccc3a28f0e898595198"
174
+ # }
143
175
  # }
144
- # }
145
- # ```
146
176
  #
147
177
  # Returns blank hash for Selenium Grid since `remote_status` gets 500 error
148
178
  #
149
- # ```ruby
150
- # {}
151
- # ```
179
+ # @example
180
+ #
181
+ # @driver.appium_server_version #=> {}
152
182
  #
153
- # @return [Hash]
154
183
  def appium_server_version
155
184
  @driver.remote_status
156
185
  rescue Selenium::WebDriver::Error::ServerError => e
@@ -161,6 +190,11 @@ module Appium
161
190
 
162
191
  # Return the platform version as an array of integers
163
192
  # @return [Array<Integer>]
193
+ #
194
+ # @example
195
+ #
196
+ # @driver.platform_version #=> [10.1.1]
197
+ #
164
198
  def platform_version
165
199
  p_version = @driver.capabilities['platformVersion']
166
200
  p_version.split('.').map(&:to_i)
@@ -168,10 +202,13 @@ module Appium
168
202
 
169
203
  # Takes a png screenshot and saves to the target path.
170
204
  #
171
- # Example: screenshot '/tmp/hi.png'
172
- #
173
205
  # @param png_save_path [String] the full path to save the png
174
206
  # @return [nil]
207
+ #
208
+ # @example
209
+ #
210
+ # @driver.screenshot '/tmp/hi.png' #=> nil
211
+ #
175
212
  def screenshot(png_save_path)
176
213
  @driver.save_screenshot png_save_path
177
214
  nil
@@ -212,6 +249,28 @@ module Appium
212
249
  target
213
250
  end
214
251
 
252
+ # @private
253
+ def validate_keys(opts)
254
+ flatten_ops = flatten_hash_keys(opts)
255
+
256
+ raise Error::NoCapabilityError unless opts.member?(:caps)
257
+ if !opts.member?(:appium_lib) && flatten_ops.member?(:appium_lib)
258
+ raise Error::CapabilityStructureError, 'Please check the value of appium_lib in the capability'
259
+ end
260
+
261
+ true
262
+ end
263
+
264
+ # @private
265
+ def flatten_hash_keys(hash, flatten_keys_result = [])
266
+ hash.each do |key, value|
267
+ flatten_keys_result << key
268
+ flatten_hash_keys(value, flatten_keys_result) if value.is_a?(Hash)
269
+ end
270
+
271
+ flatten_keys_result
272
+ end
273
+
215
274
  # @private
216
275
  def get_caps(opts)
217
276
  Core::Base::Capabilities.create_capabilities(opts[:caps] || {})
@@ -269,6 +328,14 @@ module Appium
269
328
  return unless @automation_name.nil?
270
329
  @automation_name = @driver.capabilities['automationName']
271
330
  end
272
- end
273
- end
331
+
332
+ # @private
333
+ def write_session_id(session_id)
334
+ File.open('/tmp/appium_lib_session', 'w') { |f| f.puts session_id }
335
+ rescue IOError => e
336
+ ::Appium::Logger.warn e
337
+ nil
338
+ end
339
+ end # class Driver
340
+ end # module Core
274
341
  end # module Appium