appium_lib 9.2.0 → 9.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.
@@ -30,8 +30,11 @@ module Appium
30
30
  end
31
31
 
32
32
  # Move to the given co-ordinates.
33
- # @option opts [integer] :x x co-ordinate to move to.
34
- # @option opts [integer] :y y co-ordinate to move to.
33
+ #
34
+ # `move_to`'s `x` and `y` have two case. One is working as coordinate, the other is working as offset.
35
+ #
36
+ # @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.
37
+ # @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.
35
38
  # @option opts [WebDriver::Element] Element to scope this move within.
36
39
  def move_to(opts)
37
40
  opts = args_with_ele_ref(opts)
@@ -123,39 +126,38 @@ module Appium
123
126
  #
124
127
  # Note that iOS 7 simulators have broken swipe.
125
128
  #
129
+ # For iOS: Use `offset_x` and `offset_y` to define the end point.
130
+ #
131
+ # For Android: Use `end_x` and `end_y` to define the end point.
132
+ #
133
+ # If you'd like more details, please read tests and its log samples in
134
+ # `ios_tests/lib/ios/specs/device/touch_actions.rb` and `ios_tests/lib/ios/specs/device/touch_actions.rb`
135
+ #
126
136
  # @option opts [int] :start_x Where to start swiping, on the x axis. Default 0.
127
137
  # @option opts [int] :start_y Where to start swiping, on the y axis. Default 0.
128
- # @option opts [int] :delta_x The distance from start to move, on the x axis. Default 0.
129
- # @option opts [int] :delta_y The distance from start to move, on the y axis. Default 0.
138
+ # @option opts [int] :offset_x For iOS. Offset, on the x axis. Default 0.
139
+ # @option opts [int] :offset_y For iOS. Offset, on the y axis. Default 0.
140
+ # @option opts [int] :end_x For Android. Where to end swiping, on the x axis. Default 0.
141
+ # @option opts [int] :end_y For Android. Where to end swiping, on the y axis. Default 0.
130
142
  # @option opts [int] :duration How long the actual swipe takes to complete in milliseconds. Default 200.
131
- # @deprecated Please do not use end_x, end_y anymore
132
143
  def swipe(opts, ele = nil)
133
144
  start_x = opts.fetch :start_x, 0
134
145
  start_y = opts.fetch :start_y, 0
135
- delta_x = opts.fetch :delta_x, nil
136
- delta_y = opts.fetch :delta_y, nil
146
+ offset_x = opts.fetch :offset_x, nil
147
+ offset_y = opts.fetch :offset_y, nil
137
148
  end_x = opts.fetch :end_x, nil
138
149
  end_y = opts.fetch :end_y, nil
139
-
140
- if end_x || end_y
141
- warn '[DEPRECATION] `end_x` and `end_y` are deprecated. Please use `delta_x` and `delta_y` instead.'
142
- end
143
-
144
- delta_x ||= end_x
145
- delta_y ||= end_y
146
-
147
- delta_x ||= 0
148
- delta_y ||= 0
149
-
150
150
  duration = opts.fetch :duration, 200
151
151
 
152
+ coordinates = swipe_coordinates(end_x: end_x, end_y: end_y, offset_x: offset_x, offset_y: offset_y)
153
+
152
154
  if ele # pinch/zoom for XCUITest
153
155
  press x: start_x, y: start_y, element: ele
154
- move_to x: start_x + delta_x, y: start_y + delta_y, element: ele
156
+ move_to x: coordinates[:offset_x], y: coordinates[:offset_y], element: ele
155
157
  else
156
158
  press x: start_x, y: start_y
157
159
  wait(duration) if duration
158
- move_to x: start_x + delta_x, y: start_y + delta_y
160
+ move_to x: coordinates[:offset_x], y: coordinates[:offset_y]
159
161
  end
160
162
  release
161
163
 
@@ -175,6 +177,22 @@ module Appium
175
177
  self
176
178
  end
177
179
 
180
+ def swipe_coordinates(end_x: nil, end_y: nil, offset_x: nil, offset_y: nil)
181
+ if $driver.device_is_android?
182
+ puts 'end_x and end_y are used for Android. Not offset_x and offset_y.' if end_x.nil? || end_y.nil?
183
+ end_x ||= 0
184
+ end_y ||= 0
185
+ return { offset_x: end_x, offset_y: end_y }
186
+ elsif offset_x.nil? || offset_y.nil?
187
+ puts 'offset_x and offset_y are used for iOS. Not end_x and end_y point.'
188
+ end
189
+
190
+ offset_x ||= 0
191
+ offset_y ||= 0
192
+
193
+ { offset_x: offset_x, offset_y: offset_y }
194
+ end
195
+
178
196
  private
179
197
 
180
198
  def chain_method(method, args = nil)
@@ -304,23 +304,52 @@ module Appium
304
304
  # Return http client called in start_driver()
305
305
  # @return [Selenium::WebDriver::Remote::Http::Default] the http client
306
306
  attr_reader :http_client
307
+ # Return a time wait timeout
308
+ # Wait time for ::Appium::Common.wait or ::Appium::Common.wait_true.
309
+ # Provide Appium::Drive like { appium_lib: { wait_timeout: 20 } }
310
+ # @return [Integer]
311
+ attr_reader :appium_wait_timeout
312
+ # Return a time wait timeout
313
+ # Wait interval time for ::Appium::Common.wait or ::Appium::Common.wait_true.
314
+ # Provide Appium::Drive like { appium_lib: { wait_interval: 20 } }
315
+ # @return [Integer]
316
+ attr_reader :appium_wait_interval
307
317
 
308
318
  # Creates a new driver
309
319
  #
310
- # ```ruby
311
- # require 'rubygems'
312
- # require 'appium_lib'
320
+ # @example
313
321
  #
314
- # # platformName takes a string or a symbol.
322
+ # ```ruby
323
+ # require 'rubygems'
324
+ # require 'appium_lib'
315
325
  #
316
- # # Start iOS driver
317
- # opts = { caps: { platformName: :ios, app: '/path/to/MyiOS.app' } }
318
- # Appium::Driver.new(opts).start_driver
326
+ # # platformName takes a string or a symbol.
319
327
  #
320
- # # Start Android driver
321
- # opts = { caps: { platformName: :android, app: '/path/to/my.apk' } }
322
- # Appium::Driver.new(opts).start_driver
323
- # ```
328
+ # # Start iOS driver
329
+ # opts = {
330
+ # caps: {
331
+ # platformName: :ios,
332
+ # app: '/path/to/MyiOS.app'
333
+ # },
334
+ # appium_lib: {
335
+ # wait_timeout: 30
336
+ # }
337
+ # }
338
+ # Appium::Driver.new(opts).start_driver
339
+ #
340
+ # # Start Android driver
341
+ # opts = {
342
+ # caps: {
343
+ # platformName: :android,
344
+ # app: '/path/to/my.apk'
345
+ # },
346
+ # appium_lib: {
347
+ # wait_timeout: 30,
348
+ # wait_interval: 1
349
+ # }
350
+ # }
351
+ # Appium::Driver.new(opts).start_driver
352
+ # ```
324
353
  #
325
354
  # @param opts [Object] A hash containing various options.
326
355
  # @return [Driver]
@@ -344,6 +373,9 @@ module Appium
344
373
  @sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY']
345
374
  @sauce_access_key = nil if !@sauce_access_key || (@sauce_access_key.is_a?(String) && @sauce_access_key.empty?)
346
375
  @appium_port = appium_lib_opts.fetch :port, 4723
376
+ # timeout and interval used in ::Appium::Comm.wait/wait_true
377
+ @appium_wait_timeout = appium_lib_opts.fetch :wait_timeout, 30
378
+ @appium_wait_interval = appium_lib_opts.fetch :wait_interval, 0.5
347
379
 
348
380
  # to pass it in Selenium.new.
349
381
  # `listener = opts.delete(:listener)` is called in Selenium::Driver.new
@@ -408,7 +440,9 @@ module Appium
408
440
  port: @appium_port,
409
441
  device: @appium_device,
410
442
  debug: @appium_debug,
411
- listener: @listener
443
+ listener: @listener,
444
+ wait_timeout: @appium_wait_timeout,
445
+ wait_interval: @appium_wait_interval
412
446
  }
413
447
 
414
448
  # Return duplicates so attributes are immutable
@@ -432,8 +466,8 @@ module Appium
432
466
  # If the Appium server is under REQUIRED_VERSION_XCUITEST, then error is raised.
433
467
  # @return [Boolean]
434
468
  def check_server_version_xcuitest
435
- if automation_name_is_xcuitest? && (@appium_server_version['build']['version'] <= REQUIRED_VERSION_XCUITEST)
436
- raise Appium::Error::NotSupportedAppiumServer, "XCUITest requires over Appium #{REQUIRED_VERSION_XCUITEST}"
469
+ if automation_name_is_xcuitest? && (@appium_server_version['build']['version'] < REQUIRED_VERSION_XCUITEST)
470
+ raise Appium::Error::NotSupportedAppiumServer, "XCUITest requires Appium version >= #{REQUIRED_VERSION_XCUITEST}"
437
471
  end
438
472
  true
439
473
  end
@@ -610,11 +644,11 @@ module Appium
610
644
  #
611
645
  # exists { button('sign in') } ? puts('true') : puts('false')
612
646
  #
613
- # @param pre_check [Integer] the amount in seconds to set the
614
- # wait to before checking existance
615
- # @param post_check [Integer] the amount in seconds to set the
616
- # wait to after checking existance
617
- # @param search_block [Block] the block to call
647
+ # @param [Integer] pre_check The amount in seconds to set the
648
+ # wait to before checking existence
649
+ # @param [Integer] post_check The amount in seconds to set the
650
+ # wait to after checking existence
651
+ # @yield The block to call
618
652
  # @return [Boolean]
619
653
  def exists(pre_check = 0, post_check = @default_wait)
620
654
  # do not uset set_wait here.
@@ -637,8 +671,8 @@ module Appium
637
671
  end
638
672
 
639
673
  # The same as @driver.execute_script
640
- # @param script [String] the script to execute
641
- # @param args [*args] the args to pass to the script
674
+ # @param [String] script The script to execute
675
+ # @param [*args] args The args to pass to the script
642
676
  # @return [Object]
643
677
  def execute_script(script, *args)
644
678
  @driver.execute_script script, *args
@@ -646,7 +680,7 @@ module Appium
646
680
 
647
681
  # Calls @driver.find_elements
648
682
  #
649
- # @param args [*args] the args to use
683
+ # @param [*args] args The args to use
650
684
  # @return [Array<Element>] Array is empty when no elements are found.
651
685
  def find_elements(*args)
652
686
  @driver.find_elements_with_appium(*args)
@@ -654,7 +688,7 @@ module Appium
654
688
 
655
689
  # Calls @driver.find_elements
656
690
  #
657
- # @param args [*args] the args to use
691
+ # @param [*args] args The args to use
658
692
  # @return [Element]
659
693
  def find_element(*args)
660
694
  @driver.find_element_with_appium(*args)
@@ -664,7 +698,7 @@ module Appium
664
698
  #
665
699
  # @note This method does not work on real devices.
666
700
  #
667
- # @param [Hash] opts consisting of:
701
+ # @param [Hash] opts consisting of:
668
702
  # @option opts [Float] :latitude the latitude in degrees (required)
669
703
  # @option opts [Float] :longitude the longitude in degees (required)
670
704
  # @option opts [Float] :altitude the altitude, defaulting to 75
@@ -18,7 +18,7 @@ module Appium
18
18
  return ele_index button_class, value if value.is_a? Numeric
19
19
 
20
20
  if automation_name_is_xcuitest?
21
- _raise_error_if_no_element buttons(value).first
21
+ raise_error_if_no_element buttons(value).first
22
22
  else
23
23
  ele_by_json_visible_contains button_class, value
24
24
  end
@@ -32,8 +32,8 @@ module Appium
32
32
  return tags button_class unless value
33
33
 
34
34
  if automation_name_is_xcuitest?
35
- elements = tags button_class
36
- _elements_include elements, value
35
+ visible_elements = tags button_class
36
+ elements_include visible_elements, value
37
37
  else
38
38
  eles_by_json_visible_contains button_class, value
39
39
  end
@@ -58,7 +58,7 @@ module Appium
58
58
  # @return [UIAButton|XCUIElementTypeButton]
59
59
  def button_exact(value)
60
60
  if automation_name_is_xcuitest?
61
- _raise_error_if_no_element buttons_exact(value).first
61
+ raise_error_if_no_element buttons_exact(value).first
62
62
  else
63
63
  ele_by_json_visible_exact button_class, value
64
64
  end
@@ -69,8 +69,8 @@ module Appium
69
69
  # @return [Array<UIAButton|XCUIElementTypeButton>]
70
70
  def buttons_exact(value)
71
71
  if automation_name_is_xcuitest?
72
- elements = tags button_class
73
- _elements_exact elements, value
72
+ visible_elements = tags button_class
73
+ elements_exact visible_elements, value
74
74
  else
75
75
  eles_by_json_visible_exact button_class, value
76
76
  end
@@ -5,7 +5,7 @@ module Appium
5
5
  # @return [Element]
6
6
  def find(value)
7
7
  if automation_name_is_xcuitest?
8
- find_ele_by_attr_include '*', '*', value
8
+ raise_error_if_no_element finds(value).first
9
9
  else
10
10
  ele_by_json_visible_contains '*', value
11
11
  end
@@ -16,7 +16,8 @@ module Appium
16
16
  # @return [Array<Element>]
17
17
  def finds(value)
18
18
  if automation_name_is_xcuitest?
19
- find_eles_by_attr_include '*', '*', value
19
+ elements = find_eles_by_attr_include '*', '*', value
20
+ select_visible_elements elements
20
21
  else
21
22
  eles_by_json_visible_contains '*', value
22
23
  end
@@ -27,7 +28,7 @@ module Appium
27
28
  # @return [Element]
28
29
  def find_exact(value)
29
30
  if automation_name_is_xcuitest?
30
- find_ele_by_attr '*', '*', value
31
+ raise_error_if_no_element finds_exact(value).first
31
32
  else
32
33
  ele_by_json_visible_exact '*', value
33
34
  end
@@ -38,7 +39,8 @@ module Appium
38
39
  # @return [Array<Element>]
39
40
  def finds_exact(value)
40
41
  if automation_name_is_xcuitest?
41
- find_eles_by_attr '*', '*', value
42
+ elements = find_eles_by_attr '*', '*', value
43
+ select_visible_elements elements
42
44
  else
43
45
  eles_by_json_visible_exact '*', value
44
46
  end
@@ -46,25 +48,43 @@ module Appium
46
48
 
47
49
  private
48
50
 
49
- def _raise_error_if_no_element(element)
50
- raise ::Selenium::WebDriver::Error::NoSuchElementError if element.nil?
51
+ def raise_error_if_no_element(element)
52
+ error_message = 'An element could not be located on the page using the given search parameters.'
53
+ raise(::Selenium::WebDriver::Error::NoSuchElementError, error_message) if element.nil?
51
54
  element
52
55
  end
53
56
 
54
- def _elements_include(elements, value)
57
+ # Return all elements include not displayed elements.
58
+ def elements_include(elements, value)
55
59
  return [] if elements.empty?
56
60
  elements.select do |element|
61
+ # `text` is equal to `value` if value is not `nil`
62
+ # `text` is equal to `name` if value is `nil`
57
63
  name = element.name
58
- name.nil? ? false : name.downcase.include?(value.downcase)
64
+ text = element.value
65
+ name_result = name.nil? ? false : name.downcase.include?(value.downcase)
66
+ text_result = text.nil? ? false : text.downcase.include?(value.downcase)
67
+ name_result || text_result
59
68
  end
60
69
  end
61
70
 
62
- def _elements_exact(elements, value)
71
+ # Return all elements include not displayed elements.
72
+ def elements_exact(elements, value)
63
73
  return [] if elements.empty?
64
74
  elements.select do |element|
75
+ # `text` is equal to `value` if value is not `nil`
76
+ # `text` is equal to `name` if value is `nil`
65
77
  name = element.name
66
- name.nil? ? false : name.casecmp(value).zero?
78
+ text = element.value
79
+ name_result = name.nil? ? false : name.casecmp(value).zero?
80
+ text_result = text.nil? ? false : text.casecmp(value).zero?
81
+ name_result || text_result
67
82
  end
68
83
  end
84
+
85
+ # Return visible elements.
86
+ def select_visible_elements(elements)
87
+ elements.select(&:displayed?)
88
+ end
69
89
  end # module Ios
70
90
  end # module Appium
@@ -1,12 +1,12 @@
1
1
  # UIAStaticText|XCUIElementTypeStaticText methods
2
2
  module Appium
3
3
  module Ios
4
- IAStaticText = 'UIAStaticText'.freeze
4
+ UIAStaticText = 'UIAStaticText'.freeze
5
5
  XCUIElementTypeStaticText = 'XCUIElementTypeStaticText'.freeze
6
6
 
7
7
  # @return [String] Class name for text
8
8
  def static_text_class
9
- automation_name_is_xcuitest? ? XCUIElementTypeStaticText : IAStaticText
9
+ automation_name_is_xcuitest? ? XCUIElementTypeStaticText : UIAStaticText
10
10
  end
11
11
 
12
12
  # Find the first UIAStaticText|XCUIElementTypeStaticText that contains value or by index.
@@ -17,7 +17,7 @@ module Appium
17
17
  return ele_index static_text_class, value if value.is_a? Numeric
18
18
 
19
19
  if automation_name_is_xcuitest?
20
- _raise_error_if_no_element texts(value).first
20
+ raise_error_if_no_element texts(value).first
21
21
  else
22
22
  ele_by_json_visible_contains static_text_class, value
23
23
  end
@@ -31,8 +31,8 @@ module Appium
31
31
  return tags static_text_class unless value
32
32
 
33
33
  if automation_name_is_xcuitest?
34
- elements = tags static_text_class
35
- _elements_include elements, value
34
+ visible_elements = tags static_text_class
35
+ elements_include visible_elements, value
36
36
  else
37
37
  eles_by_json_visible_contains static_text_class, value
38
38
  end
@@ -55,7 +55,7 @@ module Appium
55
55
  # @return [UIAStaticText|XCUIElementTypeStaticText]
56
56
  def text_exact(value)
57
57
  if automation_name_is_xcuitest?
58
- _raise_error_if_no_element texts_exact(value).first
58
+ raise_error_if_no_element texts_exact(value).first
59
59
  else
60
60
  ele_by_json_visible_exact static_text_class, value
61
61
  end
@@ -66,8 +66,8 @@ module Appium
66
66
  # @return [Array<UIAStaticText|XCUIElementTypeStaticText>]
67
67
  def texts_exact(value)
68
68
  if automation_name_is_xcuitest?
69
- elements = tags static_text_class
70
- _elements_exact elements, value
69
+ visible_elements = tags static_text_class
70
+ elements_exact visible_elements, value
71
71
  else
72
72
  eles_by_json_visible_exact static_text_class, value
73
73
  end
@@ -27,13 +27,14 @@ module Appium
27
27
  # @private
28
28
  # for XCUITest
29
29
  def _textfield_with_xpath
30
- xpath "//#{_textfields}"
30
+ raise_error_if_no_element _textfields_with_xpath.first
31
31
  end
32
32
 
33
33
  # @private
34
34
  # for XCUITest
35
35
  def _textfields_with_xpath
36
- xpaths "//#{_textfields}"
36
+ elements = xpaths "//#{_textfields}"
37
+ select_visible_elements elements
37
38
  end
38
39
 
39
40
  # Appium
@@ -78,7 +79,7 @@ module Appium
78
79
  end
79
80
 
80
81
  if automation_name_is_xcuitest?
81
- find_ele_by_attr_include _textfields, '*', value
82
+ raise_error_if_no_element textfields(value).first
82
83
  else
83
84
  ele_by_json _textfield_contains_string value
84
85
  end
@@ -91,7 +92,8 @@ module Appium
91
92
  def textfields(value = false)
92
93
  if automation_name_is_xcuitest?
93
94
  return _textfields_with_xpath unless value
94
- find_eles_by_attr_include _textfields, '*', value
95
+ elements = find_eles_by_attr_include _textfields, '*', value
96
+ select_visible_elements elements
95
97
  else
96
98
  return eles_by_json _textfield_visible unless value
97
99
  eles_by_json _textfield_contains_string value
@@ -125,7 +127,7 @@ module Appium
125
127
  # @return [TextField]
126
128
  def textfield_exact(value)
127
129
  if automation_name_is_xcuitest?
128
- find_ele_by_attr _textfields, '*', value
130
+ raise_error_if_no_element textfields_exact(value).first
129
131
  else
130
132
  ele_by_json _textfield_exact_string value
131
133
  end
@@ -136,7 +138,8 @@ module Appium
136
138
  # @return [Array<TextField>]
137
139
  def textfields_exact(value)
138
140
  if automation_name_is_xcuitest?
139
- find_eles_by_attr _textfields, '*', value
141
+ elements = find_eles_by_attr _textfields, '*', value
142
+ select_visible_elements elements
140
143
  else
141
144
  eles_by_json _textfield_exact_string value
142
145
  end