appium_lib 3.0.3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/android_tests/Rakefile +0 -3
- data/android_tests/appium.txt +1 -0
- data/android_tests/lib/android/specs/android/element/alert.rb +9 -2
- data/android_tests/lib/android/specs/android/element/textfield.rb +5 -0
- data/android_tests/lib/android/specs/android/helper.rb +6 -15
- data/android_tests/lib/android/specs/android/patch.rb +16 -3
- data/android_tests/lib/android/specs/common/device.rb +41 -41
- data/android_tests/lib/android/specs/common/patch.rb +1 -1
- data/android_tests/lib/android/specs/common/web_context.rb +55 -7
- data/android_tests/lib/android/specs/driver.rb +4 -3
- data/android_tests/lib/android/specs/install.rb +25 -0
- data/android_tests/lib/run.rb +4 -3
- data/docs/android_docs.md +357 -187
- data/docs/docs.md +8 -28
- data/docs/ios_docs.md +341 -179
- data/docs/migration.md +6 -0
- data/ios_tests/Rakefile +0 -3
- data/ios_tests/appium.txt +1 -0
- data/ios_tests/lib/common.rb +30 -0
- data/ios_tests/lib/ios/specs/device/device.rb +6 -1
- data/ios_tests/lib/ios/specs/driver.rb +3 -3
- data/ios_tests/lib/ios/specs/ios/element/alert.rb +0 -5
- data/ios_tests/lib/ios/specs/ios/element/textfield.rb +10 -0
- data/ios_tests/lib/run.rb +4 -106
- data/lib/appium_lib.rb +2 -2
- data/lib/appium_lib/android/element/button.rb +16 -26
- data/lib/appium_lib/android/element/generic.rb +15 -12
- data/lib/appium_lib/android/helper.rb +39 -53
- data/lib/appium_lib/common/patch.rb +10 -39
- data/lib/appium_lib/common/version.rb +2 -2
- data/lib/appium_lib/device/device.rb +54 -70
- data/lib/appium_lib/driver.rb +29 -19
- data/lib/appium_lib/ios/helper.rb +50 -1
- data/lib/appium_lib/ios/patch.rb +1 -23
- data/release_notes.md +38 -0
- metadata +4 -4
- data/android_tests/lib/android/specs/android/dynamic.rb +0 -5
- data/lib/appium_lib/android/dynamic.rb +0 -47
@@ -89,47 +89,18 @@ def patch_webdriver_bridge
|
|
89
89
|
path_str = path.sub(path_match[0], '') unless path_match.nil?
|
90
90
|
|
91
91
|
puts "#{verb} #{path_str}"
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
print_command[:args] = [command_hash.clone]
|
97
|
-
print_command[:script] = 'complex_find' if command == :complex_find
|
98
|
-
else
|
99
|
-
print_command = command_hash.clone
|
100
|
-
end
|
92
|
+
# must check to see if command_hash is a hash. sometimes it's not.
|
93
|
+
if command_hash.is_a?(Hash) && !command_hash.empty?
|
94
|
+
print_command = command_hash.clone
|
95
|
+
|
101
96
|
print_command.delete :args if print_command[:args] == []
|
102
97
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
#
|
110
|
-
# [[[[4, "android.widget.EditText"], [7, "z"]], [[4, "android.widget.EditText"], [3, "z"]]]]
|
111
|
-
# => [[[4, "android.widget.EditText"], [7, "z"]], [[4, "android.widget.EditText"], [3, "z"]]]
|
112
|
-
args = args[0]
|
113
|
-
option = args[0].to_s.downcase
|
114
|
-
has_option = !option.match(/all|scroll/).nil?
|
115
|
-
puts option if has_option
|
116
|
-
|
117
|
-
start = has_option ? 1 : 0
|
118
|
-
|
119
|
-
start.upto(args.length-1) do |selector_index|
|
120
|
-
selectors = args[selector_index]
|
121
|
-
selectors_size = selectors.length
|
122
|
-
selectors.each_index do |pair_index|
|
123
|
-
pair = selectors[pair_index]
|
124
|
-
res = $driver.dynamic_code_to_string pair[0], pair[1]
|
125
|
-
|
126
|
-
if selectors_size == 1 || pair_index >= selectors_size - 1
|
127
|
-
puts res
|
128
|
-
elsif selectors_size > 1 && pair_index < selectors_size
|
129
|
-
print res + '.'
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end # start.upto
|
98
|
+
if print_command[:using] === '-android uiautomator'
|
99
|
+
value = print_command[:value].split(';').map { |v| "#{v};" }
|
100
|
+
print_command[:value] = value.length == 1 ? value[0] : value
|
101
|
+
|
102
|
+
# avoid backslash escape quotes in strings. "\"a\"" => "a"
|
103
|
+
puts print_command.ai.gsub('\"', '"')
|
133
104
|
else
|
134
105
|
ap print_command
|
135
106
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Appium
|
2
2
|
# Version and Date are defined on the 'Appium' module, not 'Appium::Common'
|
3
|
-
VERSION = '
|
4
|
-
DATE = '2014-
|
3
|
+
VERSION = '4.0.0' unless defined? ::Appium::VERSION
|
4
|
+
DATE = '2014-07-05' unless defined? ::Appium::DATE
|
5
5
|
end
|
@@ -6,6 +6,7 @@ module Appium
|
|
6
6
|
|
7
7
|
NoArgMethods = {
|
8
8
|
post: {
|
9
|
+
open_notifications: 'session/:session_id/appium/device/open_notifications',
|
9
10
|
shake: 'session/:session_id/appium/device/shake',
|
10
11
|
launch: 'session/:session_id/appium/app/launch',
|
11
12
|
close_app: 'session/:session_id/appium/app/close',
|
@@ -15,7 +16,6 @@ module Appium
|
|
15
16
|
get: {
|
16
17
|
current_activity: 'session/:session_id/appium/device/current_activity',
|
17
18
|
current_context: 'session/:session_id/context',
|
18
|
-
available_contexts: 'session/:session_id/contexts',
|
19
19
|
}
|
20
20
|
}
|
21
21
|
|
@@ -44,18 +44,6 @@ module Appium
|
|
44
44
|
# @!method toggle_flight_mode
|
45
45
|
# toggle flight mode on or off
|
46
46
|
|
47
|
-
#@!method complex_find
|
48
|
-
# Find an element by a complex array of criteria. Available criteria
|
49
|
-
# are listed in [link here]. Criteria are formed by creating an array
|
50
|
-
# of arrays, each containing a selector and that selector's value.
|
51
|
-
#
|
52
|
-
# ```ruby
|
53
|
-
# complex_find [[[2, 'Sau'], [14, true]]] # => Find a clickable element
|
54
|
-
# # whose names starts with 'Sau'
|
55
|
-
# ```
|
56
|
-
# @param mod (Symbol) If present, will be the 0th element in the selector array.
|
57
|
-
# @param selectors (Array<Object>) The selectors to find elements with.
|
58
|
-
|
59
47
|
# @!method hide_keyboard
|
60
48
|
# Hide the onscreen keyboard
|
61
49
|
# @param close_key (String) the name of the key which closes the keyboard.
|
@@ -65,10 +53,17 @@ module Appium
|
|
65
53
|
# hide_keyboard('Finished') # Close a keyboard with the 'Finished' button
|
66
54
|
# ```
|
67
55
|
|
68
|
-
# @!method
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# @param
|
56
|
+
# @!method press_keycode
|
57
|
+
# Press keycode on the device.
|
58
|
+
# http://developer.android.com/reference/android/view/KeyEvent.html
|
59
|
+
# @param key (integer) The key to press.
|
60
|
+
# @param metastate (String) The state the metakeys should be in when pressing the key.
|
61
|
+
|
62
|
+
# @!method long_press_keycode
|
63
|
+
# Long press keycode on the device.
|
64
|
+
# http://developer.android.com/reference/android/view/KeyEvent.html
|
65
|
+
# @param key (integer) The key to long press.
|
66
|
+
# @param metastate (String) The state the metakeys should be in when long pressing the key.
|
72
67
|
|
73
68
|
# @!method push_file
|
74
69
|
# Place a file in a specific location on the device.
|
@@ -85,6 +80,14 @@ module Appium
|
|
85
80
|
# pull_file 'Shenanigans.app/some/file' #=> Get 'some/file' from the install location of Shenanigans.app
|
86
81
|
# ```
|
87
82
|
|
83
|
+
# @!method pull_folder
|
84
|
+
# Retrieve a folder from the device.
|
85
|
+
# @param path (String) absolute path to the folder
|
86
|
+
#
|
87
|
+
# ```ruby
|
88
|
+
# pull_folder '/data/local/tmp' #=> Get the folder at that path
|
89
|
+
# ```
|
90
|
+
|
88
91
|
# @!method end_coverage
|
89
92
|
# Android only; Ends the test coverage and writes the results to the given path on device.
|
90
93
|
# @param path (String) Path on the device to write too.
|
@@ -97,6 +100,13 @@ module Appium
|
|
97
100
|
pair.each_pair { |command, path| add_endpoint_method command, path, verb }
|
98
101
|
end
|
99
102
|
|
103
|
+
add_endpoint_method(:available_contexts, 'session/:session_id/contexts', :get) do
|
104
|
+
def available_contexts
|
105
|
+
# return empty array instead of nil on failure
|
106
|
+
execute(:available_contexts, {}) || []
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
100
110
|
add_endpoint_method(:app_strings, 'session/:session_id/appium/app/strings') do
|
101
111
|
def app_strings language=nil
|
102
112
|
opts = language ? { language: language } : {}
|
@@ -141,16 +151,30 @@ module Appium
|
|
141
151
|
end
|
142
152
|
|
143
153
|
add_endpoint_method(:hide_keyboard, 'session/:session_id/appium/device/hide_keyboard') do
|
144
|
-
def hide_keyboard(close_key=
|
145
|
-
|
154
|
+
def hide_keyboard(close_key=nil)
|
155
|
+
# Android can only tapOutside.
|
156
|
+
if $driver.device_is_android?
|
157
|
+
return execute :hide_keyboard, {}, { strategy: :tapOutside }
|
158
|
+
end
|
159
|
+
|
160
|
+
close_key ||= 'Done' # default to Done key.
|
161
|
+
$driver.hide_ios_keyboard close_key
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
add_endpoint_method(:press_keycode, 'session/:session_id/appium/device/press_keycode') do
|
166
|
+
def press_keycode(key, metastate=nil)
|
167
|
+
args = { keycode: key }
|
168
|
+
args[:metastate] = metastate if metastate
|
169
|
+
execute :press_keycode, {}, args
|
146
170
|
end
|
147
171
|
end
|
148
172
|
|
149
|
-
add_endpoint_method(:
|
150
|
-
def
|
173
|
+
add_endpoint_method(:long_press_keycode, 'session/:session_id/appium/device/long_press_keycode') do
|
174
|
+
def long_press_keycode(key, metastate=nil)
|
151
175
|
args = { keycode: key }
|
152
176
|
args[:metastate] = metastate if metastate
|
153
|
-
execute :
|
177
|
+
execute :long_press_keycode, {}, args
|
154
178
|
end
|
155
179
|
end
|
156
180
|
|
@@ -161,30 +185,6 @@ module Appium
|
|
161
185
|
end
|
162
186
|
end
|
163
187
|
|
164
|
-
add_endpoint_method(:complex_find, 'session/:session_id/appium/app/complex_find') do
|
165
|
-
def complex_find(opts)
|
166
|
-
# allow: complex_find([[[3, 'app']]])
|
167
|
-
opts = { selectors: opts } if opts.is_a?(Array)
|
168
|
-
mode = opts.fetch :mode, false # mode can be 'all' or 'scroll'
|
169
|
-
selectors = opts.fetch :selectors, false
|
170
|
-
raise 'Complex find must have selectors' unless selectors
|
171
|
-
selectors = selectors.dup.insert(0, mode) if mode # must dupe before insert
|
172
|
-
|
173
|
-
ids = execute :complex_find, {}, selectors
|
174
|
-
ids = [ids] unless ids.is_a? Array # wrap single result in an array
|
175
|
-
|
176
|
-
# mode can be set directly in the selectors
|
177
|
-
find_all = mode == 'all' || selectors.first == 'all'
|
178
|
-
|
179
|
-
result = ids.map do |id|
|
180
|
-
resolved_id = element_id_from(id)
|
181
|
-
Selenium::WebDriver::Element.new self, resolved_id
|
182
|
-
end
|
183
|
-
# when not finding all, don't return an array
|
184
|
-
return find_all ? result : result.first
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
188
|
add_endpoint_method(:push_file, 'session/:session_id/appium/device/push_file') do
|
189
189
|
def push_file(path, filedata)
|
190
190
|
encoded_data = Base64.encode64 filedata
|
@@ -199,6 +199,14 @@ module Appium
|
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
202
|
+
# TODO TEST ME
|
203
|
+
add_endpoint_method(:pull_folder, 'session/:session_id/appium/device/pull_folder') do
|
204
|
+
def pull_folder(path)
|
205
|
+
data = execute :pull_folder, {}, path: path
|
206
|
+
Base64.decode64 data
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
202
210
|
# TODO TEST ME
|
203
211
|
add_endpoint_method(:end_coverage, 'session/:session_id/appium/app/end_test_coverage') do
|
204
212
|
def end_coverage(path, intent)
|
@@ -247,12 +255,6 @@ module Appium
|
|
247
255
|
|
248
256
|
# @private
|
249
257
|
def create_bridge_command(method, verb, path)
|
250
|
-
# Don't clobber methods that are moved into Selenium
|
251
|
-
if selenium_has method
|
252
|
-
log_reimplemented_warning(method, path)
|
253
|
-
return
|
254
|
-
end
|
255
|
-
|
256
258
|
Selenium::WebDriver::Remote::Bridge.class_eval do
|
257
259
|
command method, verb, path
|
258
260
|
if block_given?
|
@@ -263,24 +265,6 @@ module Appium
|
|
263
265
|
end
|
264
266
|
end
|
265
267
|
|
266
|
-
# @private
|
267
|
-
def selenium_has(method)
|
268
|
-
Selenium::WebDriver::Remote::Bridge.method_defined? method
|
269
|
-
end
|
270
|
-
|
271
|
-
# @private
|
272
|
-
def log_reimplemented_warning(method, path)
|
273
|
-
msg = "Selenium::WebDriver has now implemented the `#{method}` method."
|
274
|
-
if Selenium::WebDriver::Remote::COMMANDS[method][1] == path
|
275
|
-
msg << " It may no longer function as expected"
|
276
|
-
else
|
277
|
-
msg << " It no longer uses the same endpoint,"
|
278
|
-
msg << " so it probably won't do what you expect anymore."
|
279
|
-
end
|
280
|
-
msg << " Raise an issue at http://www.github.com/appium/ruby_lib if so."
|
281
|
-
Appium::Logger.warn msg
|
282
|
-
end
|
283
|
-
|
284
268
|
# @!method accessiblity_id_find
|
285
269
|
# find_element/s with their accessibility_id
|
286
270
|
#
|
data/lib/appium_lib/driver.rb
CHANGED
@@ -24,7 +24,6 @@ require_relative 'ios/element/text'
|
|
24
24
|
require_relative 'ios/mobile_methods'
|
25
25
|
|
26
26
|
# android
|
27
|
-
require_relative 'android/dynamic'
|
28
27
|
require_relative 'android/helper'
|
29
28
|
require_relative 'android/patch'
|
30
29
|
require_relative 'android/element/alert'
|
@@ -210,7 +209,17 @@ module Appium
|
|
210
209
|
# made available via the driver_attributes method
|
211
210
|
|
212
211
|
# The amount to sleep in seconds before every webdriver http call.
|
213
|
-
attr_accessor :global_webdriver_http_sleep
|
212
|
+
attr_accessor :global_webdriver_http_sleep,
|
213
|
+
:caps,
|
214
|
+
:custom_url,
|
215
|
+
:export_session,
|
216
|
+
:default_wait,
|
217
|
+
:last_waits,
|
218
|
+
:sauce_username,
|
219
|
+
:sauce_access_key,
|
220
|
+
:appium_port,
|
221
|
+
:appium_device,
|
222
|
+
:appium_debug
|
214
223
|
|
215
224
|
# Creates a new driver
|
216
225
|
#
|
@@ -251,7 +260,7 @@ module Appium
|
|
251
260
|
@sauce_username = nil if !@sauce_username || (@sauce_username.is_a?(String) && @sauce_username.empty?)
|
252
261
|
@sauce_access_key = appium_lib_opts.fetch :sauce_access_key, ENV['SAUCE_ACCESS_KEY']
|
253
262
|
@sauce_access_key = nil if !@sauce_access_key || (@sauce_access_key.is_a?(String) && @sauce_access_key.empty?)
|
254
|
-
@
|
263
|
+
@appium_port = appium_lib_opts.fetch :port, 4723
|
255
264
|
|
256
265
|
# Path to the .apk, .app or .app.zip.
|
257
266
|
# The path can be local or remote for Sauce.
|
@@ -260,13 +269,14 @@ module Appium
|
|
260
269
|
end
|
261
270
|
|
262
271
|
# https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile
|
263
|
-
@
|
264
|
-
@
|
265
|
-
raise "platformName must be set. Not found in options: #{opts}" unless @
|
266
|
-
raise 'platformName must be Android or iOS' unless [:android, :ios].include?(@
|
272
|
+
@appium_device = @caps[:platformName]
|
273
|
+
@appium_device = @appium_device.is_a?(Symbol) ? @appium_device : @appium_device.downcase.strip.intern if @appium_device
|
274
|
+
raise "platformName must be set. Not found in options: #{opts}" unless @appium_device
|
275
|
+
raise 'platformName must be Android or iOS' unless [:android, :ios].include?(@appium_device)
|
267
276
|
|
268
277
|
# load common methods
|
269
278
|
extend Appium::Common
|
279
|
+
extend Appium::Device
|
270
280
|
if device_is_android?
|
271
281
|
# load Android specific methods
|
272
282
|
extend Appium::Android
|
@@ -280,12 +290,12 @@ module Appium
|
|
280
290
|
|
281
291
|
# enable debug patch
|
282
292
|
# !!'constant' == true
|
283
|
-
@
|
293
|
+
@appium_debug = appium_lib_opts.fetch :debug, !!defined?(Pry)
|
284
294
|
|
285
|
-
if @
|
295
|
+
if @appium_debug
|
286
296
|
ap opts unless opts.empty?
|
287
|
-
puts "Debug is: #{@
|
288
|
-
puts "Device is: #{@
|
297
|
+
puts "Debug is: #{@appium_debug}"
|
298
|
+
puts "Device is: #{@appium_device}"
|
289
299
|
patch_webdriver_bridge
|
290
300
|
end
|
291
301
|
|
@@ -297,9 +307,6 @@ module Appium
|
|
297
307
|
# Subsequent drivers do not trigger promotion.
|
298
308
|
unless @@loaded
|
299
309
|
@@loaded = true
|
300
|
-
# load device methods exactly once
|
301
|
-
extend Appium::Device
|
302
|
-
|
303
310
|
# Promote only on Minitest::Spec (minitest 5) by default
|
304
311
|
Appium.promote_appium_methods ::Minitest::Spec
|
305
312
|
end
|
@@ -316,9 +323,9 @@ module Appium
|
|
316
323
|
last_waits: @last_waits,
|
317
324
|
sauce_username: @sauce_username,
|
318
325
|
sauce_access_key: @sauce_access_key,
|
319
|
-
port: @
|
320
|
-
device: @
|
321
|
-
debug: @
|
326
|
+
port: @appium_port,
|
327
|
+
device: @appium_device,
|
328
|
+
debug: @appium_debug,
|
322
329
|
}
|
323
330
|
|
324
331
|
# Return duplicates so attributes are immutable
|
@@ -329,7 +336,7 @@ module Appium
|
|
329
336
|
end
|
330
337
|
|
331
338
|
def device_is_android?
|
332
|
-
@
|
339
|
+
@appium_device == :android
|
333
340
|
end
|
334
341
|
|
335
342
|
# Returns the server's version info
|
@@ -352,6 +359,9 @@ module Appium
|
|
352
359
|
# @return [String] APP_PATH as an absolute path
|
353
360
|
def self.absolute_app_path app_path
|
354
361
|
raise 'APP_PATH not set!' if app_path.nil? || app_path.empty?
|
362
|
+
# may be absolute path to file on remote server.
|
363
|
+
# if the file is on the remote server then we can't check if it exists
|
364
|
+
return app_path if @custom_url
|
355
365
|
# Sauce storage API. http://saucelabs.com/docs/rest#storage
|
356
366
|
return app_path if app_path.start_with? 'sauce-storage:'
|
357
367
|
return app_path if app_path.match(/^http/) # public URL for Sauce
|
@@ -379,7 +389,7 @@ module Appium
|
|
379
389
|
if !@sauce_username.nil? && !@sauce_access_key.nil?
|
380
390
|
"http://#{@sauce_username}:#{@sauce_access_key}@ondemand.saucelabs.com:80/wd/hub"
|
381
391
|
else
|
382
|
-
"http://127.0.0.1:#{@
|
392
|
+
"http://127.0.0.1:#{@appium_port}/wd/hub"
|
383
393
|
end
|
384
394
|
end
|
385
395
|
|
@@ -132,7 +132,7 @@ module Appium
|
|
132
132
|
end
|
133
133
|
# current_context may be nil which breaks start_with
|
134
134
|
if current_context && current_context.start_with?('WEBVIEW')
|
135
|
-
s
|
135
|
+
s = get_source
|
136
136
|
parser = @android_html_parser ||= Nokogiri::HTML::SAX::Parser.new(Common::HTMLElements.new)
|
137
137
|
parser.document.reset
|
138
138
|
parser.document.filter = class_name
|
@@ -354,5 +354,54 @@ module Appium
|
|
354
354
|
def xpaths_visible_exact element, value
|
355
355
|
xpaths string_visible_exact element, value
|
356
356
|
end
|
357
|
+
|
358
|
+
# @private
|
359
|
+
# If there's no keyboard, then do nothing.
|
360
|
+
# If there's no close key, fallback to window tap.
|
361
|
+
# If close key is present then tap it.
|
362
|
+
# @param close_key [String] close key to tap. Default value is 'Done'
|
363
|
+
# @return [void]
|
364
|
+
def hide_ios_keyboard close_key='Done'
|
365
|
+
=begin
|
366
|
+
Find the top left corner of the keyboard and move up 10 pixels (origin.y - 10)
|
367
|
+
now swipe down until the end of the window - 10 pixels.
|
368
|
+
-10 to ensure we're not going outside the window bounds.
|
369
|
+
|
370
|
+
Swiping inside the keyboard will not dismiss it.
|
371
|
+
|
372
|
+
Don't use window.tap. See https://github.com/appium/appium-uiauto/issues/28
|
373
|
+
=end
|
374
|
+
dismiss_keyboard = (<<-JS).strip
|
375
|
+
if (!au.mainApp().keyboard().isNil()) {
|
376
|
+
var key = au.mainApp().keyboard().buttons()['#{close_key}']
|
377
|
+
if (key.isNil()) {
|
378
|
+
var startY = au.mainApp().keyboard().rect().origin.y - 10;
|
379
|
+
var endY = au.mainWindow().rect().size.height - 10;
|
380
|
+
au.flickApp(0, startY, 0, endY);
|
381
|
+
} else {
|
382
|
+
key.tap();
|
383
|
+
}
|
384
|
+
au.delay(1000);
|
385
|
+
}
|
386
|
+
JS
|
387
|
+
|
388
|
+
ignore do
|
389
|
+
# wait 5 seconds for a wild keyboard to appear. if the textfield is disabled
|
390
|
+
# then setValue will work, however the keyboard will never display
|
391
|
+
# because users are normally not allowed to type into it.
|
392
|
+
wait_true(5) do
|
393
|
+
execute_script '!au.mainApp().keyboard().isNil()'
|
394
|
+
end
|
395
|
+
|
396
|
+
# dismiss keyboard
|
397
|
+
execute_script dismiss_keyboard
|
398
|
+
end
|
399
|
+
|
400
|
+
# wait 5 seconds for keyboard to go away.
|
401
|
+
# if the keyboard isn't dismissed then wait_true will error.
|
402
|
+
wait_true(5) do
|
403
|
+
execute_script 'au.mainApp().keyboard().isNil()'
|
404
|
+
end
|
405
|
+
end
|
357
406
|
end # module Ios
|
358
407
|
end # module Appium
|