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