appium_lib 3.0.3 → 4.0.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/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
|