appium_lib 9.0.0 → 9.1.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +21 -0
- data/android_tests/lib/android/specs/android/helper.rb +3 -5
- data/android_tests/lib/android/specs/common/command.rb +47 -0
- data/android_tests/lib/android/specs/common/helper.rb +8 -8
- data/android_tests/lib/android/specs/common/web_context.rb +1 -1
- data/android_tests/lib/android/specs/driver.rb +19 -11
- data/android_tests/lib/format.rb +3 -3
- data/android_tests/lib/run.rb +7 -5
- data/android_tests/readme.md +1 -1
- data/appium_lib.gemspec +6 -7
- data/contributing.md +10 -0
- data/docs/android_docs.md +252 -217
- data/docs/ios_docs.md +257 -222
- data/docs/ios_xcuitest.md +32 -4
- data/ios_tests/lib/common.rb +11 -55
- data/ios_tests/lib/ios/specs/common/command.rb +44 -0
- data/ios_tests/lib/ios/specs/common/helper.rb +8 -8
- data/ios_tests/lib/ios/specs/device/device.rb +5 -5
- data/ios_tests/lib/ios/specs/driver.rb +34 -15
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +1 -1
- data/ios_tests/readme.md +1 -1
- data/ios_tests/upload/sauce_storage.rb +10 -8
- data/lib/appium_lib/android/element/button.rb +3 -3
- data/lib/appium_lib/android/element/text.rb +1 -1
- data/lib/appium_lib/android/element/textfield.rb +1 -1
- data/lib/appium_lib/android/helper.rb +12 -12
- data/lib/appium_lib/android/mobile_methods.rb +1 -3
- data/lib/appium_lib/common/command.rb +55 -0
- data/lib/appium_lib/common/helper.rb +7 -7
- data/lib/appium_lib/common/patch.rb +21 -8
- data/lib/appium_lib/common/search_context.rb +10 -0
- data/lib/appium_lib/common/version.rb +2 -2
- data/lib/appium_lib/common/wait.rb +8 -14
- data/lib/appium_lib/device/device.rb +65 -83
- data/lib/appium_lib/device/multi_touch.rb +2 -2
- data/lib/appium_lib/device/touch_actions.rb +4 -7
- data/lib/appium_lib/driver.rb +66 -38
- data/lib/appium_lib/ios/element/alert.rb +4 -4
- data/lib/appium_lib/ios/element/textfield.rb +3 -3
- data/lib/appium_lib/ios/errors.rb +6 -0
- data/lib/appium_lib/ios/helper.rb +25 -25
- data/lib/appium_lib/ios/mobile_methods.rb +2 -4
- data/readme.md +3 -1
- data/release_notes.md +13 -0
- metadata +23 -12
@@ -26,7 +26,7 @@ module Appium
|
|
26
26
|
# action.perform #=> to 25% of its size.
|
27
27
|
# ```
|
28
28
|
def pinch(percentage = 25, auto_perform = true)
|
29
|
-
|
29
|
+
raise ArgumentError("Can't pinch to greater than screen size.") if percentage > 100
|
30
30
|
|
31
31
|
p = Float(percentage) / 100
|
32
32
|
|
@@ -62,7 +62,7 @@ module Appium
|
|
62
62
|
# action.perform
|
63
63
|
# ```
|
64
64
|
def zoom(percentage = 200, auto_perform = true)
|
65
|
-
|
65
|
+
raise ArgumentError("Can't zoom to smaller then screen size.") if percentage < 100
|
66
66
|
|
67
67
|
p = 100 / Float(percentage)
|
68
68
|
i = 1 - p
|
@@ -8,8 +8,8 @@ module Appium
|
|
8
8
|
# action = TouchAction.new.press(x: 45, y: 100).wait(5).release
|
9
9
|
# action.perform
|
10
10
|
class TouchAction
|
11
|
-
ACTIONS = [:move_to, :long_press, :double_tap, :two_finger_tap, :press, :release, :tap, :wait, :perform]
|
12
|
-
COMPLEX_ACTIONS = [:swipe]
|
11
|
+
ACTIONS = [:move_to, :long_press, :double_tap, :two_finger_tap, :press, :release, :tap, :wait, :perform].freeze
|
12
|
+
COMPLEX_ACTIONS = [:swipe].freeze
|
13
13
|
|
14
14
|
class << self
|
15
15
|
COMPLEX_ACTIONS.each do |action|
|
@@ -174,11 +174,8 @@ module Appium
|
|
174
174
|
private
|
175
175
|
|
176
176
|
def chain_method(method, args = nil)
|
177
|
-
|
178
|
-
|
179
|
-
else
|
180
|
-
@actions << { action: method }
|
181
|
-
end
|
177
|
+
action = args ? { action: method, options: args } : { action: method }
|
178
|
+
@actions << action
|
182
179
|
self
|
183
180
|
end
|
184
181
|
|
data/lib/appium_lib/driver.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'ap'
|
3
3
|
require 'selenium-webdriver'
|
4
|
-
require 'selenium/client/errors' # used in helper.rb for CommandError
|
5
4
|
require 'nokogiri'
|
6
5
|
|
7
6
|
# common
|
@@ -10,11 +9,14 @@ require_relative 'common/wait'
|
|
10
9
|
require_relative 'common/patch'
|
11
10
|
require_relative 'common/version'
|
12
11
|
require_relative 'common/error'
|
12
|
+
require_relative 'common/search_context'
|
13
|
+
require_relative 'common/command'
|
13
14
|
require_relative 'common/element/window'
|
14
15
|
|
15
16
|
# ios
|
16
17
|
require_relative 'ios/helper'
|
17
18
|
require_relative 'ios/patch'
|
19
|
+
require_relative 'ios/errors'
|
18
20
|
|
19
21
|
require_relative 'ios/element/alert'
|
20
22
|
require_relative 'ios/element/button'
|
@@ -70,11 +72,11 @@ module Appium
|
|
70
72
|
# @param opts [Hash] file: '/path/to/appium.txt', verbose: true
|
71
73
|
# @return [hash] the symbolized hash with updated :app and :require keys
|
72
74
|
def self.load_settings(opts = {})
|
73
|
-
|
74
|
-
|
75
|
+
raise 'opts must be a hash' unless opts.is_a? Hash
|
76
|
+
raise 'opts must not be empty' if opts.empty?
|
75
77
|
|
76
78
|
toml = opts[:file]
|
77
|
-
|
79
|
+
raise 'Must pass file' unless toml
|
78
80
|
verbose = opts.fetch :verbose, false
|
79
81
|
|
80
82
|
Appium::Logger.info "appium settings path: #{toml}" if verbose
|
@@ -82,12 +84,14 @@ module Appium
|
|
82
84
|
toml_exists = File.exist? toml
|
83
85
|
Appium::Logger.info "Exists? #{toml_exists}" if verbose
|
84
86
|
|
85
|
-
|
87
|
+
raise "toml doesn't exist #{toml}" unless toml_exists
|
86
88
|
require 'tomlrb'
|
87
89
|
Appium::Logger.info "Loading #{toml}" if verbose
|
88
90
|
|
89
91
|
data = Tomlrb.load_file(toml, symbolize_keys: true)
|
90
|
-
|
92
|
+
if verbose
|
93
|
+
Appium::Logger.ap_info data unless data.empty?
|
94
|
+
end
|
91
95
|
|
92
96
|
if data && data[:caps] && data[:caps][:app] && !data[:caps][:app].empty?
|
93
97
|
data[:caps][:app] = Appium::Driver.absolute_app_path data
|
@@ -102,6 +106,7 @@ module Appium
|
|
102
106
|
end
|
103
107
|
|
104
108
|
class << self
|
109
|
+
# rubocop:disable Style/Alias
|
105
110
|
alias_method :load_appium_txt, :load_settings
|
106
111
|
end
|
107
112
|
|
@@ -141,7 +146,7 @@ module Appium
|
|
141
146
|
# based on deep_symbolize_keys & deep_transform_keys from rails
|
142
147
|
# https://github.com/rails/docrails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/activesupport/lib/active_support/core_ext/hash/keys.rb#L84
|
143
148
|
def self.symbolize_keys(hash)
|
144
|
-
|
149
|
+
raise 'symbolize_keys requires a hash' unless hash.is_a? Hash
|
145
150
|
result = {}
|
146
151
|
hash.each do |key, value|
|
147
152
|
key = key.to_sym rescue key # rubocop:disable Style/RescueModifier
|
@@ -162,7 +167,7 @@ module Appium
|
|
162
167
|
# that module are promoted on.
|
163
168
|
# otherwise, the array of modules will be used as the promotion target.
|
164
169
|
def self.promote_singleton_appium_methods(modules)
|
165
|
-
|
170
|
+
raise 'Driver is nil' if $driver.nil?
|
166
171
|
|
167
172
|
target_modules = []
|
168
173
|
|
@@ -171,12 +176,13 @@ module Appium
|
|
171
176
|
target_modules << modules.const_get(sub_module)
|
172
177
|
end
|
173
178
|
else
|
174
|
-
|
179
|
+
raise 'modules must be a module or an array' unless modules.is_a? Array
|
175
180
|
target_modules = modules
|
176
181
|
end
|
177
182
|
|
178
183
|
target_modules.each do |const|
|
179
184
|
# noinspection RubyResolve
|
185
|
+
# rubocop:disable Style/MultilineIfModifier
|
180
186
|
$driver.public_methods(false).each do |m|
|
181
187
|
const.send(:define_singleton_method, m) do |*args, &block|
|
182
188
|
begin
|
@@ -187,6 +193,7 @@ module Appium
|
|
187
193
|
# override unless there's an existing method with matching arity
|
188
194
|
end unless const.respond_to?(m) && const.method(m).arity == $driver.method(m).arity
|
189
195
|
end
|
196
|
+
# rubocop:enable Style/MultilineIfModifier
|
190
197
|
end
|
191
198
|
end
|
192
199
|
|
@@ -213,7 +220,7 @@ module Appium
|
|
213
220
|
# Appium.promote_appium_methods Minitest::Spec
|
214
221
|
# ```
|
215
222
|
def self.promote_appium_methods(class_array)
|
216
|
-
|
223
|
+
raise 'Driver is nil' if $driver.nil?
|
217
224
|
# Wrap single class into an array
|
218
225
|
class_array = [class_array] unless class_array.class == Array
|
219
226
|
# Promote Appium driver methods to class instance methods.
|
@@ -239,6 +246,22 @@ module Appium
|
|
239
246
|
nil # return nil
|
240
247
|
end
|
241
248
|
|
249
|
+
class Driver
|
250
|
+
module Capabilities
|
251
|
+
# except for browser_name, default capability is equal to ::Selenium::WebDriver::Remote::Capabilities.firefox
|
252
|
+
# Because Selenium::WebDriver::Remote::Bridge uses Capabilities.firefox by default
|
253
|
+
# https://github.com/SeleniumHQ/selenium/blob/selenium-3.0.1/rb/lib/selenium/webdriver/remote/bridge.rb#L67
|
254
|
+
def self.init_caps_for_appium(opts_caps = {})
|
255
|
+
default_caps_opts_firefox = {
|
256
|
+
javascript_enabled: true,
|
257
|
+
takes_screenshot: true,
|
258
|
+
css_selectors_enabled: true
|
259
|
+
}.merge(opts_caps)
|
260
|
+
::Selenium::WebDriver::Remote::Capabilities.new(default_caps_opts_firefox)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
242
265
|
class Driver
|
243
266
|
# attr readers are promoted to global scope. To avoid clobbering, they're
|
244
267
|
# made available via the driver_attributes method
|
@@ -302,12 +325,12 @@ module Appium
|
|
302
325
|
def initialize(opts = {})
|
303
326
|
# quit last driver
|
304
327
|
$driver.driver_quit if $driver
|
305
|
-
|
328
|
+
raise 'opts must be a hash' unless opts.is_a? Hash
|
306
329
|
|
307
330
|
opts = Appium.symbolize_keys opts
|
308
331
|
|
309
|
-
|
310
|
-
|
332
|
+
@caps = Capabilities.init_caps_for_appium(opts[:caps] || {})
|
333
|
+
|
311
334
|
appium_lib_opts = opts[:appium_lib] || {}
|
312
335
|
|
313
336
|
# appium_lib specific values
|
@@ -327,7 +350,7 @@ module Appium
|
|
327
350
|
|
328
351
|
# Path to the .apk, .app or .app.zip.
|
329
352
|
# The path can be local or remote for Sauce.
|
330
|
-
if @caps && @caps[:app] &&
|
353
|
+
if @caps && @caps[:app] && !@caps[:app].empty?
|
331
354
|
@caps[:app] = self.class.absolute_app_path opts
|
332
355
|
end
|
333
356
|
|
@@ -351,6 +374,9 @@ module Appium
|
|
351
374
|
# apply os specific patches
|
352
375
|
patch_webdriver_element
|
353
376
|
|
377
|
+
# for command
|
378
|
+
patch_remote_driver_commands
|
379
|
+
|
354
380
|
# enable debug patch
|
355
381
|
# !!'constant' == true
|
356
382
|
@appium_debug = appium_lib_opts.fetch :debug, !!defined?(Pry)
|
@@ -370,17 +396,18 @@ module Appium
|
|
370
396
|
|
371
397
|
# Returns a hash of the driver attributes
|
372
398
|
def driver_attributes
|
373
|
-
attributes = {
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
399
|
+
attributes = {
|
400
|
+
caps: @caps,
|
401
|
+
custom_url: @custom_url,
|
402
|
+
export_session: @export_session,
|
403
|
+
default_wait: @default_wait,
|
404
|
+
last_waits: @last_waits,
|
405
|
+
sauce_username: @sauce_username,
|
406
|
+
sauce_access_key: @sauce_access_key,
|
407
|
+
port: @appium_port,
|
408
|
+
device: @appium_device,
|
409
|
+
debug: @appium_debug,
|
410
|
+
listener: @listener
|
384
411
|
}
|
385
412
|
|
386
413
|
# Return duplicates so attributes are immutable
|
@@ -397,7 +424,7 @@ module Appium
|
|
397
424
|
# Return true if automationName is 'XCUITest'
|
398
425
|
# @return [Boolean]
|
399
426
|
def automation_name_is_xcuitest?
|
400
|
-
!@automation_name.nil? && @automation_name.
|
427
|
+
!@automation_name.nil? && 'xcuitest'.casecmp(@automation_name).zero?
|
401
428
|
end
|
402
429
|
|
403
430
|
# Return true if the target Appium server is over REQUIRED_VERSION_XCUITEST.
|
@@ -405,7 +432,7 @@ module Appium
|
|
405
432
|
# @return [Boolean]
|
406
433
|
def check_server_version_xcuitest
|
407
434
|
if automation_name_is_xcuitest? && (@appium_server_version['build']['version'] <= REQUIRED_VERSION_XCUITEST)
|
408
|
-
|
435
|
+
raise Appium::Error::NotSupportedAppiumServer, "XCUITest requires over Appium #{REQUIRED_VERSION_XCUITEST}"
|
409
436
|
end
|
410
437
|
true
|
411
438
|
end
|
@@ -435,33 +462,33 @@ module Appium
|
|
435
462
|
#
|
436
463
|
# @return [String] APP_PATH as an absolute path
|
437
464
|
def self.absolute_app_path(opts)
|
438
|
-
|
465
|
+
raise 'opts must be a hash' unless opts.is_a? Hash
|
439
466
|
caps = opts[:caps] || {}
|
440
467
|
appium_lib_opts = opts[:appium_lib] || {}
|
441
468
|
server_url = appium_lib_opts.fetch :server_url, false
|
442
469
|
|
443
470
|
app_path = caps[:app]
|
444
|
-
|
471
|
+
raise 'absolute_app_path invoked and app is not set!' if app_path.nil? || app_path.empty?
|
445
472
|
# may be absolute path to file on remote server.
|
446
473
|
# if the file is on the remote server then we can't check if it exists
|
447
474
|
return app_path if server_url
|
448
475
|
# Sauce storage API. http://saucelabs.com/docs/rest#storage
|
449
476
|
return app_path if app_path.start_with? 'sauce-storage:'
|
450
|
-
return app_path if app_path
|
451
|
-
if app_path
|
477
|
+
return app_path if app_path =~ /^http/ # public URL for Sauce
|
478
|
+
if app_path =~ /^(\/|[a-zA-Z]:)/ # absolute file path
|
452
479
|
app_path = File.expand_path app_path unless File.exist? app_path
|
453
|
-
|
480
|
+
raise "App doesn't exist. #{app_path}" unless File.exist? app_path
|
454
481
|
return app_path
|
455
482
|
end
|
456
483
|
|
457
484
|
# if it doesn't contain a slash then it's a bundle id
|
458
|
-
return app_path unless app_path
|
485
|
+
return app_path unless app_path =~ /[\/\\]/
|
459
486
|
|
460
487
|
# relative path that must be expanded.
|
461
488
|
# absolute_app_path is called from load_settings
|
462
489
|
# and the txt file path is the base of the app path in that case.
|
463
490
|
app_path = File.expand_path app_path
|
464
|
-
|
491
|
+
raise "App doesn't exist #{app_path}" unless File.exist? app_path
|
465
492
|
app_path
|
466
493
|
end
|
467
494
|
|
@@ -498,8 +525,9 @@ module Appium
|
|
498
525
|
# @return [void]
|
499
526
|
def driver_quit
|
500
527
|
# rescue NoSuchDriverError or nil driver
|
501
|
-
|
502
|
-
|
528
|
+
@driver.quit
|
529
|
+
rescue
|
530
|
+
nil
|
503
531
|
end
|
504
532
|
|
505
533
|
# Creates a new global driver and quits the old one if it exists.
|
@@ -621,7 +649,7 @@ module Appium
|
|
621
649
|
# @param args [*args] the args to use
|
622
650
|
# @return [Array<Element>] Array is empty when no elements are found.
|
623
651
|
def find_elements(*args)
|
624
|
-
@driver.
|
652
|
+
@driver.find_elements_with_appium(*args)
|
625
653
|
end
|
626
654
|
|
627
655
|
# Calls @driver.find_elements
|
@@ -629,7 +657,7 @@ module Appium
|
|
629
657
|
# @param args [*args] the args to use
|
630
658
|
# @return [Element]
|
631
659
|
def find_element(*args)
|
632
|
-
@driver.
|
660
|
+
@driver.find_element_with_appium(*args)
|
633
661
|
end
|
634
662
|
|
635
663
|
# Calls @driver.set_location
|
@@ -4,16 +4,16 @@ module Appium
|
|
4
4
|
# @return [void]
|
5
5
|
def alert_accept
|
6
6
|
# @driver.switch_to.alert.accept
|
7
|
-
# ".switch_to.alert" calls
|
8
|
-
driver.send(:bridge).
|
7
|
+
# ".switch_to.alert" calls alert_text so use bridge directly
|
8
|
+
driver.send(:bridge).accept_alert
|
9
9
|
end
|
10
10
|
|
11
11
|
# Dismiss the alert.
|
12
12
|
# @return [void]
|
13
13
|
def alert_dismiss
|
14
14
|
# @driver.switch_to.alert.dismiss
|
15
|
-
# ".switch_to.alert" calls
|
16
|
-
driver.send(:bridge).
|
15
|
+
# ".switch_to.alert" calls alert_text so use bridge directly
|
16
|
+
driver.send(:bridge).dismiss_alert
|
17
17
|
end
|
18
18
|
end # module Ios
|
19
19
|
end # module Appium
|
@@ -65,14 +65,14 @@ module Appium
|
|
65
65
|
def textfield(value)
|
66
66
|
if value.is_a? Numeric
|
67
67
|
index = value
|
68
|
-
|
68
|
+
raise "#{index} is not a valid index. Must be >= 1" if index <= 0
|
69
69
|
index -= 1 # eles_by_json and _textfields_with_xpath is 0 indexed.
|
70
70
|
result = if automation_name_is_xcuitest?
|
71
71
|
_textfields_with_xpath[index]
|
72
72
|
else
|
73
73
|
eles_by_json(_textfield_visible)[index]
|
74
74
|
end
|
75
|
-
|
75
|
+
raise _no_such_element if result.nil?
|
76
76
|
return result
|
77
77
|
|
78
78
|
end
|
@@ -116,7 +116,7 @@ module Appium
|
|
116
116
|
else
|
117
117
|
eles_by_json(_textfield_visible).last
|
118
118
|
end
|
119
|
-
|
119
|
+
raise _no_such_element if result.nil?
|
120
120
|
result
|
121
121
|
end
|
122
122
|
|
@@ -14,21 +14,21 @@ module Appium
|
|
14
14
|
|
15
15
|
def _print_attr(type, name, label, value, hint)
|
16
16
|
if name == label && name == value
|
17
|
-
puts
|
17
|
+
puts type.to_s if name || label || value || hint
|
18
18
|
puts " name, label, value: #{name}" if name
|
19
19
|
puts " hint: #{hint}" if hint
|
20
20
|
elsif name == label
|
21
|
-
puts
|
21
|
+
puts type.to_s if name || label || value || hint
|
22
22
|
puts " name, label: #{name}" if name
|
23
23
|
puts " value: #{value}" if value
|
24
24
|
puts " hint: #{hint}" if hint
|
25
25
|
elsif name == value
|
26
|
-
puts
|
26
|
+
puts type.to_s if name || label || value || hint
|
27
27
|
puts " name, value: #{name}" if name
|
28
28
|
puts " label: #{label}" if label
|
29
29
|
puts " hint: #{hint}" if hint
|
30
30
|
else
|
31
|
-
puts
|
31
|
+
puts type.to_s if name || label || value || hint
|
32
32
|
puts " name: #{name}" if name
|
33
33
|
puts " label: #{label}" if label
|
34
34
|
puts " value: #{value}" if value
|
@@ -91,24 +91,24 @@ module Appium
|
|
91
91
|
# rubocop:disable Metrics/BlockNesting
|
92
92
|
|
93
93
|
# if class_name is set, mark non-matches as invisible
|
94
|
-
visible = (type.downcase.include?
|
94
|
+
visible = (type.downcase.include? class_namet).to_s if class_name
|
95
95
|
if visible && visible == 'true'
|
96
96
|
if name == label && name == value
|
97
|
-
puts
|
97
|
+
puts type.to_s if name || label || value || hint
|
98
98
|
puts " name, label, value: #{name}" if name
|
99
99
|
puts " hint: #{hint}" if hint
|
100
100
|
elsif name == label
|
101
|
-
puts
|
101
|
+
puts type.to_s if name || label || value || hint
|
102
102
|
puts " name, label: #{name}" if name
|
103
103
|
puts " value: #{value}" if value
|
104
104
|
puts " hint: #{hint}" if hint
|
105
105
|
elsif name == value
|
106
|
-
puts
|
106
|
+
puts type.to_s if name || label || value || hint
|
107
107
|
puts " name, value: #{name}" if name
|
108
108
|
puts " label: #{label}" if label
|
109
109
|
puts " hint: #{hint}" if hint
|
110
110
|
else
|
111
|
-
puts
|
111
|
+
puts type.to_s if name || label || value || hint
|
112
112
|
puts " name: #{name}" if name
|
113
113
|
puts " label: #{label}" if label
|
114
114
|
puts " value: #{value}" if value
|
@@ -236,7 +236,7 @@ module Appium
|
|
236
236
|
# @param index [Integer] the index
|
237
237
|
# @return [Element]
|
238
238
|
def ele_index(class_name, index)
|
239
|
-
|
239
|
+
raise 'Index must be >= 1' unless index == 'last()' || (index.is_a?(Integer) && index >= 1)
|
240
240
|
elements = tags(class_name)
|
241
241
|
|
242
242
|
if index == 'last()'
|
@@ -247,7 +247,7 @@ module Appium
|
|
247
247
|
result = elements[index]
|
248
248
|
end
|
249
249
|
|
250
|
-
|
250
|
+
raise _no_such_element if result.nil?
|
251
251
|
result
|
252
252
|
end
|
253
253
|
|
@@ -334,7 +334,7 @@ module Appium
|
|
334
334
|
def last_ele(class_name)
|
335
335
|
if automation_name_is_xcuitest?
|
336
336
|
result = @driver.find_elements :xpath, "(//#{class_name})"
|
337
|
-
|
337
|
+
raise _no_such_element if result.empty?
|
338
338
|
result.last
|
339
339
|
else
|
340
340
|
ele_index class_name, 'last()'
|
@@ -469,7 +469,7 @@ module Appium
|
|
469
469
|
#
|
470
470
|
# Don't use window.tap. See https://github.com/appium/appium-uiauto/issues/28
|
471
471
|
#
|
472
|
-
dismiss_keyboard =
|
472
|
+
dismiss_keyboard = <<-JS.strip
|
473
473
|
if (!au.mainApp().keyboard().isNil()) {
|
474
474
|
var key = au.mainApp().keyboard().buttons()['#{close_key}']
|
475
475
|
if (key.isNil()) {
|
@@ -509,7 +509,7 @@ module Appium
|
|
509
509
|
#
|
510
510
|
def _all_pred(opts)
|
511
511
|
predicate = opts[:predicate]
|
512
|
-
|
512
|
+
raise 'predicate must be provided' unless predicate
|
513
513
|
visible = opts.fetch :visible, true
|
514
514
|
%($.mainApp().getAllWithPredicate("#{predicate}", #{visible});)
|
515
515
|
end
|
@@ -542,22 +542,22 @@ module Appium
|
|
542
542
|
end
|
543
543
|
|
544
544
|
def _validate_object(*objects)
|
545
|
-
|
545
|
+
raise 'objects must be an array' unless objects.is_a? Array
|
546
546
|
objects.each do |obj|
|
547
547
|
next unless obj # obj may be nil. if so, ignore.
|
548
548
|
|
549
549
|
valid_keys = [:target, :substring, :insensitive]
|
550
550
|
unknown_keys = obj.keys - valid_keys
|
551
|
-
|
551
|
+
raise "Unknown keys: #{unknown_keys}" unless unknown_keys.empty?
|
552
552
|
|
553
553
|
target = obj[:target]
|
554
|
-
|
554
|
+
raise 'target must be a string' unless target.is_a? String
|
555
555
|
|
556
556
|
substring = obj[:substring]
|
557
|
-
|
557
|
+
raise 'substring must be a boolean' unless [true, false].include? substring
|
558
558
|
|
559
559
|
insensitive = obj[:insensitive]
|
560
|
-
|
560
|
+
raise 'insensitive must be a boolean' unless [true, false].include? insensitive
|
561
561
|
end
|
562
562
|
end
|
563
563
|
|
@@ -593,16 +593,16 @@ module Appium
|
|
593
593
|
def _by_json(opts)
|
594
594
|
valid_keys = [:typeArray, :onlyFirst, :onlyVisible, :name, :label, :value]
|
595
595
|
unknown_keys = opts.keys - valid_keys
|
596
|
-
|
596
|
+
raise "Unknown keys: #{unknown_keys}" unless unknown_keys.empty?
|
597
597
|
|
598
598
|
type_array = opts[:typeArray]
|
599
|
-
|
599
|
+
raise 'typeArray must be an array' unless type_array.is_a? Array
|
600
600
|
|
601
601
|
only_first = opts[:onlyFirst]
|
602
|
-
|
602
|
+
raise 'onlyFirst must be a boolean' unless [true, false].include? only_first
|
603
603
|
|
604
604
|
only_visible = opts[:onlyVisible]
|
605
|
-
|
605
|
+
raise 'onlyVisible must be a boolean' unless [true, false].include? only_visible
|
606
606
|
|
607
607
|
# name/label/value are optional. when searching for class only, then none
|
608
608
|
# will be present.
|
@@ -625,7 +625,7 @@ module Appium
|
|
625
625
|
JS
|
626
626
|
|
627
627
|
res = execute_script element_or_elements_by_type
|
628
|
-
res ? res :
|
628
|
+
res ? res : raise(Appium::Ios::CommandError, 'mainWindow is nil')
|
629
629
|
end
|
630
630
|
|
631
631
|
# For Appium(automation name), not XCUITest
|
@@ -649,7 +649,7 @@ module Appium
|
|
649
649
|
def ele_by_json(opts)
|
650
650
|
opts[:onlyFirst] = true
|
651
651
|
result = _by_json(opts).first
|
652
|
-
|
652
|
+
raise _no_such_element if result.nil?
|
653
653
|
result
|
654
654
|
end
|
655
655
|
|