calabash 1.9.9.pre3 → 2.0.0.prelegacy

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -33
  3. data/bin/calabash +45 -36
  4. data/lib/calabash.rb +137 -13
  5. data/lib/calabash/android.rb +6 -0
  6. data/lib/calabash/android/adb.rb +25 -1
  7. data/lib/calabash/android/application.rb +14 -3
  8. data/lib/calabash/android/build/builder.rb +16 -3
  9. data/lib/calabash/android/build/java_keystore.rb +10 -0
  10. data/lib/calabash/android/build/resigner.rb +23 -1
  11. data/lib/calabash/android/build/test_server.rb +2 -0
  12. data/lib/calabash/android/defaults.rb +1 -0
  13. data/lib/calabash/android/device.rb +55 -3
  14. data/lib/calabash/android/environment.rb +10 -0
  15. data/lib/calabash/android/interactions.rb +106 -3
  16. data/lib/calabash/android/legacy.rb +143 -0
  17. data/lib/calabash/android/lib/TestServer.apk +0 -0
  18. data/lib/calabash/android/life_cycle.rb +6 -4
  19. data/lib/calabash/android/physical_buttons.rb +12 -1
  20. data/lib/calabash/android/screenshot.rb +1 -0
  21. data/lib/calabash/android/scroll.rb +115 -0
  22. data/lib/calabash/android/server.rb +3 -1
  23. data/lib/calabash/android/text.rb +16 -25
  24. data/lib/calabash/application.rb +29 -0
  25. data/lib/calabash/cli/build.rb +15 -1
  26. data/lib/calabash/cli/console.rb +9 -5
  27. data/lib/calabash/cli/generate.rb +5 -0
  28. data/lib/calabash/cli/helpers.rb +7 -1
  29. data/lib/calabash/cli/resign.rb +1 -0
  30. data/lib/calabash/cli/run.rb +10 -6
  31. data/lib/calabash/cli/setup_keystore.rb +2 -0
  32. data/lib/calabash/color.rb +7 -0
  33. data/lib/calabash/console_helpers.rb +4 -2
  34. data/lib/calabash/defaults.rb +1 -0
  35. data/lib/calabash/device.rb +8 -9
  36. data/lib/calabash/environment.rb +5 -0
  37. data/lib/calabash/gestures.rb +159 -66
  38. data/lib/calabash/http/retriable_client.rb +3 -1
  39. data/lib/calabash/interactions.rb +68 -5
  40. data/lib/calabash/ios.rb +4 -0
  41. data/lib/calabash/ios/application.rb +8 -1
  42. data/lib/calabash/ios/conditions.rb +3 -1
  43. data/lib/calabash/ios/date_picker.rb +412 -0
  44. data/lib/calabash/ios/defaults.rb +1 -0
  45. data/lib/calabash/ios/device.rb +1 -0
  46. data/lib/calabash/ios/device/device_implementation.rb +33 -16
  47. data/lib/calabash/ios/device/gestures_mixin.rb +202 -48
  48. data/lib/calabash/ios/device/ipad_1x_2x_mixin.rb +253 -0
  49. data/lib/calabash/ios/device/keyboard_mixin.rb +2 -0
  50. data/lib/calabash/ios/device/rotation_mixin.rb +11 -8
  51. data/lib/calabash/ios/device/routes/condition_route_mixin.rb +1 -0
  52. data/lib/calabash/ios/device/routes/handle_route_mixin.rb +5 -1
  53. data/lib/calabash/ios/device/routes/map_route_mixin.rb +1 -0
  54. data/lib/calabash/ios/device/routes/response_parser.rb +1 -0
  55. data/lib/calabash/ios/device/routes/uia_route_mixin.rb +44 -6
  56. data/lib/calabash/ios/device/runtime_attributes.rb +4 -5
  57. data/lib/calabash/ios/device/text_mixin.rb +2 -0
  58. data/lib/calabash/ios/device/uia_keyboard_mixin.rb +9 -0
  59. data/lib/calabash/ios/device/uia_mixin.rb +1 -0
  60. data/lib/calabash/ios/gestures.rb +82 -8
  61. data/lib/calabash/ios/interactions.rb +30 -1
  62. data/lib/calabash/ios/orientation.rb +21 -21
  63. data/lib/calabash/ios/runtime.rb +154 -2
  64. data/lib/calabash/ios/slider.rb +70 -0
  65. data/lib/calabash/ios/text.rb +11 -47
  66. data/lib/calabash/ios/uia.rb +24 -2
  67. data/lib/calabash/legacy.rb +7 -0
  68. data/lib/calabash/lib/skeleton/config/cucumber.yml +1 -3
  69. data/lib/calabash/lib/skeleton/features/support/dry_run.rb +8 -0
  70. data/lib/calabash/lib/skeleton/features/support/env.rb +15 -1
  71. data/lib/calabash/life_cycle.rb +78 -32
  72. data/lib/calabash/location.rb +2 -1
  73. data/lib/calabash/orientation.rb +0 -1
  74. data/lib/calabash/page.rb +51 -5
  75. data/lib/calabash/patch.rb +1 -0
  76. data/lib/calabash/patch/array.rb +7 -7
  77. data/lib/calabash/query.rb +17 -2
  78. data/lib/calabash/query_result.rb +14 -0
  79. data/lib/calabash/screenshot.rb +28 -8
  80. data/lib/calabash/text.rb +105 -8
  81. data/lib/calabash/utility.rb +6 -6
  82. data/lib/calabash/version.rb +1 -1
  83. data/lib/calabash/wait.rb +37 -11
  84. metadata +14 -7
@@ -8,7 +8,7 @@ module Calabash
8
8
  application_path = Environment::APP_PATH
9
9
 
10
10
  if application_path.nil?
11
- raise 'No application path is set'
11
+ raise 'No application path is set. Specify application with environment variable CAL_APP'
12
12
  end
13
13
 
14
14
  unless File.exist?(application_path)
@@ -20,8 +20,12 @@ module Calabash
20
20
  end
21
21
 
22
22
  build_test_server = Build::TestServer.new(application_path)
23
- test_server_path = Environment::TEST_SERVER_PATH ||
24
- build_test_server.path
23
+ test_server_path = Environment::TEST_SERVER_PATH
24
+
25
+ unless test_server_path
26
+ test_server_path = build_test_server.path
27
+ Logger.info "No test-server specified. Defaulting to path '#{test_server_path}'"
28
+ end
25
29
 
26
30
  unless File.exist?(test_server_path)
27
31
  Logger.error "Test-server '#{test_server_path}' does not exist."
@@ -38,6 +42,13 @@ module Calabash
38
42
  @test_server = Application.new(test_server_path, nil, options) if test_server_path
39
43
  end
40
44
 
45
+ # Is this application an android application
46
+ #
47
+ # @return [Boolean] Always returns true
48
+ def android_application?
49
+ true
50
+ end
51
+
41
52
  def extract_identifier
42
53
  package_line = aapt_dump('package').first
43
54
  raise "'package' not found in aapt output" unless package_line
@@ -16,7 +16,8 @@ module Calabash
16
16
  end
17
17
  end
18
18
 
19
- def build
19
+ # @!visibility private
20
+ def build(out_file = nil)
20
21
  apk_fingerprint = fingerprint_from_apk
21
22
  @logger.log "#{@application_path} was signed with a certificate with fingerprint #{apk_fingerprint}", :debug
22
23
 
@@ -37,7 +38,7 @@ module Calabash
37
38
  end
38
39
  @logger.log ""
39
40
  @logger.log "You can resign the app with #{keystores.first.location} by running:
40
- calabash resign #{@application_path}"
41
+ calabash resign \"#{@application_path}\""
41
42
 
42
43
  @logger.log ""
43
44
  @logger.log "Notice that resigning an app might break some functionality."
@@ -48,7 +49,7 @@ module Calabash
48
49
 
49
50
  application = Calabash::Application.from_path(@application_path)
50
51
 
51
- test_server_file_name = TestServer.new(@application_path).path
52
+ test_server_file_name = out_file || TestServer.new(@application_path).path
52
53
  FileUtils.mkdir_p File.dirname(test_server_file_name) unless File.exist? File.dirname(test_server_file_name)
53
54
 
54
55
  Dir.mktmpdir do |workspace_dir|
@@ -69,6 +70,17 @@ module Calabash
69
70
 
70
71
  Zip::File.new("dummy.apk").extract("AndroidManifest.xml","customAndroidManifest.xml")
71
72
  Zip::File.open("TestServer.apk") do |zip_file|
73
+ begin
74
+ check_file("AndroidManifest.xml")
75
+ manifest_exists = true
76
+ rescue
77
+ manifest_exists = false
78
+ end
79
+
80
+ if manifest_exists
81
+ zip_file.remove("AndroidManifest.xml")
82
+ end
83
+
72
84
  zip_file.add("AndroidManifest.xml", "customAndroidManifest.xml")
73
85
  end
74
86
  end
@@ -83,6 +95,7 @@ module Calabash
83
95
  @logger.log "Done signing the test server. Moved it to #{test_server_file_name}"
84
96
  end
85
97
 
98
+ # @!visibility private
86
99
  def fingerprint_from_apk
87
100
  application_path = File.expand_path(@application_path)
88
101
 
@@ -5,6 +5,7 @@ module Calabash
5
5
  module Build
6
6
  # @!visibility private
7
7
  class JavaKeystore
8
+ # @!visibility private
8
9
  CALABASH_KEYSTORE_SETTINGS_FILENAME = 'calabash_keystore_settings.json'
9
10
 
10
11
  attr_reader :errors, :location, :keystore_alias, :store_password
@@ -68,6 +69,7 @@ module Calabash
68
69
  @logger.log "Signature algorithm name: #{signature_algorithm_name}", :debug
69
70
  end
70
71
 
72
+ # @!visibility private
71
73
  def sign_apk(apk_path, dest_path)
72
74
  raise "Cannot sign with a miss configured keystore" if errors
73
75
  raise "No such file: #{apk_path}" unless File.exists?(apk_path)
@@ -86,6 +88,7 @@ module Calabash
86
88
  end
87
89
  end
88
90
 
91
+ # @!visibility private
89
92
  def system_with_stdout_on_success(cmd, *args)
90
93
  a = Escape.shell_command(args)
91
94
  cmd = "#{cmd} #{a.gsub("'", '"')}"
@@ -98,10 +101,12 @@ module Calabash
98
101
  end
99
102
  end
100
103
 
104
+ # @!visibility private
101
105
  def fail_wrong_info
102
106
  raise "Could not read keystore with the given credentials. Please ensure "
103
107
  end
104
108
 
109
+ # @!visibility private
105
110
  def self.read_keystore_with_default_password_and_alias(path)
106
111
  path = File.expand_path path
107
112
 
@@ -128,6 +133,7 @@ module Calabash
128
133
  end
129
134
  end
130
135
 
136
+ # @!visibility private
131
137
  def self.get_keystores
132
138
  if keystore = keystore_from_settings
133
139
  [ keystore ]
@@ -141,6 +147,7 @@ module Calabash
141
147
  end
142
148
  end
143
149
 
150
+ # @!visibility private
144
151
  def self.keystore_from_settings
145
152
  if File.exist?(CALABASH_KEYSTORE_SETTINGS_FILENAME)
146
153
  Logger.info "Reading keystore information specified in #{CALABASH_KEYSTORE_SETTINGS_FILENAME}"
@@ -157,16 +164,19 @@ module Calabash
157
164
  end
158
165
  end
159
166
 
167
+ # @!visibility private
160
168
  def self.fail_if_key_missing(map, key)
161
169
  raise "Found #{CALABASH_KEYSTORE_SETTINGS_FILENAME} but no #{key} defined." unless map[key]
162
170
  end
163
171
 
172
+ # @!visibility private
164
173
  def self.extract_md5_fingerprint(fingerprints)
165
174
  m = fingerprints.scan(/MD5.*((?:[a-fA-F\d]{2}:){15}[a-fA-F\d]{2})/).flatten
166
175
  raise "No MD5 fingerprint found:\n #{fingerprints}" if m.empty?
167
176
  m.first
168
177
  end
169
178
 
179
+ # @!visibility private
170
180
  def self.extract_signature_algorithm_name(fingerprints)
171
181
  m = fingerprints.scan(/Signature algorithm name: (.*)/).flatten
172
182
  raise "No signature algorithm names found:\n #{fingerprints}" if m.empty?
@@ -14,6 +14,7 @@ module Calabash
14
14
  end
15
15
  end
16
16
 
17
+ # @!visibility private
17
18
  def resign!
18
19
  Dir.mktmpdir do |tmp_dir|
19
20
  @logger.log 'Resigning apk', :debug
@@ -26,8 +27,27 @@ module Calabash
26
27
  end
27
28
  end
28
29
 
30
+ # @!visibility private
29
31
  def unsign(unsigned_path)
30
- files_to_remove = `"#{Environment.aapt_path}" list "#{unsigned_path}"`.lines.collect(&:strip).grep(/^META-INF\//)
32
+ meta_files = `"#{Environment.aapt_path}" list "#{unsigned_path}"`.lines.collect(&:strip).grep(/^META-INF\//)
33
+
34
+ signing_file_names = ['.mf', '.rsa', '.dsa', '.ec', '.sf']
35
+
36
+ files_to_remove = meta_files.select do |file|
37
+ # other will be:
38
+ # META-INF/foo/bar
39
+ # other #=> bar
40
+ directory, file_name, other = file.split('/')
41
+
42
+ if other != nil || file_name.nil?
43
+ false
44
+ else
45
+ if signing_file_names.include?(File.extname(file_name).downcase)
46
+ true
47
+ end
48
+ end
49
+ end
50
+
31
51
  if files_to_remove.empty?
32
52
  @logger.log "App wasn't signed. Will not try to unsign it.", :debug
33
53
  else
@@ -35,12 +55,14 @@ module Calabash
35
55
  end
36
56
  end
37
57
 
58
+ # @!visibility private
38
59
  def zipalign(unaligned_path, app_path)
39
60
  cmd = %Q("#{Environment.zipalign_path}" -f 4 "#{unaligned_path}" "#{app_path}")
40
61
  @logger.log "Zipaligning using: #{cmd}", :debug
41
62
  system(cmd)
42
63
  end
43
64
 
65
+ # @!visibility private
44
66
  def sign(app_path, dest_path)
45
67
  java_keystore = JavaKeystore.get_keystores.first
46
68
 
@@ -7,10 +7,12 @@ module Calabash
7
7
  @application_path = application_path
8
8
  end
9
9
 
10
+ # @!visibility private
10
11
  def path
11
12
  File.expand_path("test_servers/#{checksum(@application_path)}_#{VERSION}.apk")
12
13
  end
13
14
 
15
+ # @!visibility private
14
16
  def exists?
15
17
  File.exists?(path)
16
18
  end
@@ -1,5 +1,6 @@
1
1
  module Calabash
2
2
  module Android
3
+ # Runtime defaults for Android.
3
4
  module Defaults
4
5
  # Sets up the default device and the default application based on the
5
6
  # environment.
@@ -22,6 +22,7 @@ module Calabash
22
22
  port_forward(new_server.endpoint.port, new_server.test_server_port)
23
23
  end
24
24
 
25
+ # @!visibility private
25
26
  def self.default_serial
26
27
  serials = list_serials
27
28
 
@@ -46,6 +47,7 @@ module Calabash
46
47
  end
47
48
  end
48
49
 
50
+ # @!visibility private
49
51
  def self.list_serials
50
52
  output = ADB.command('devices')
51
53
  lines = output.lines
@@ -62,12 +64,14 @@ module Calabash
62
64
  end
63
65
  end
64
66
 
67
+ # @!visibility private
65
68
  def installed_packages
66
69
  adb.shell('pm list packages').lines.map do |line|
67
70
  line.sub('package:', '').chomp
68
71
  end
69
72
  end
70
73
 
74
+ # @!visibility private
71
75
  def installed_apps
72
76
  adb.shell('pm list packages -f').lines.map do |line|
73
77
  # line will be package:<path>=<package>
@@ -80,6 +84,7 @@ module Calabash
80
84
  end
81
85
  end
82
86
 
87
+ # @!visibility private
83
88
  def test_server_responding?
84
89
  begin
85
90
  http_client.get(HTTP::Request.new('ping'), retries: 1).body == 'pong'
@@ -88,6 +93,7 @@ module Calabash
88
93
  end
89
94
  end
90
95
 
96
+ # @!visibility private
91
97
  def test_server_ready?
92
98
  begin
93
99
  http_client.get(HTTP::Request.new('ready')).body == 'true'
@@ -96,6 +102,7 @@ module Calabash
96
102
  end
97
103
  end
98
104
 
105
+ # @!visibility private
99
106
  def port_forward(host_port, test_server_port = nil)
100
107
  if test_server_port.nil?
101
108
  test_server_port = server.test_server_port
@@ -105,6 +112,7 @@ module Calabash
105
112
  adb.command(*adb_forward_cmd)
106
113
  end
107
114
 
115
+ # @!visibility private
108
116
  def make_map_parameters(query, map_method_name, *method_args)
109
117
  converted_args = []
110
118
 
@@ -157,7 +165,13 @@ module Calabash
157
165
 
158
166
  request = HTTP::Request.new('map', params_for_request(parameters))
159
167
 
160
- result = JSON.parse(http_client.get(request).body)
168
+ http_result = if method_name == :flash
169
+ http_client.get(request, timeout: 30)
170
+ else
171
+ http_client.get(request)
172
+ end
173
+
174
+ result = JSON.parse(http_result.body)
161
175
 
162
176
  if result['outcome'] != 'SUCCESS'
163
177
  raise "mapping \"#{query}\" with \"#{method_name}\" failed because: #{result['reason']}\n#{result['details']}"
@@ -166,6 +180,7 @@ module Calabash
166
180
  Calabash::QueryResult.create(result['results'], query)
167
181
  end
168
182
 
183
+ # @!visibility private
169
184
  def perform_action(action, *arguments)
170
185
  @logger.log "Action: #{action} - Arguments: #{arguments.join(', ')}"
171
186
 
@@ -191,10 +206,12 @@ module Calabash
191
206
  result
192
207
  end
193
208
 
209
+ # @!visibility private
194
210
  def enter_text(text)
195
211
  perform_action('keyboard_enter_text', text)
196
212
  end
197
213
 
214
+ # @!visibility private
198
215
  def md5_checksum(file_path)
199
216
  result = adb.shell("#{md5_binary} '#{file_path}'")
200
217
  captures = result.match(/(\w+)/).captures
@@ -228,15 +245,41 @@ module Calabash
228
245
  result['result']
229
246
  end
230
247
 
248
+
249
+ # @!visibility private
250
+ def keyboard_visible?
251
+ input_method = adb.shell("dumpsys input_method")
252
+ input_method.lines.each do |line|
253
+ match = line.match(/mInputShown\s*=\s*(.*)/)
254
+
255
+ if match && match.captures.length != 0
256
+ shown = match.captures.first.chomp
257
+
258
+ if shown == "true"
259
+ return true
260
+ elsif shown == "false"
261
+ return false
262
+ else
263
+ raise "Could not detect keyboard visibility. '#{shown}'"
264
+ end
265
+ end
266
+ end
267
+
268
+ raise "Could not detect keyboard visibility. Could not find 'mInputShown'"
269
+ end
270
+
271
+ # @!visibility private
231
272
  def go_home
232
273
  adb.shell("input keyevent 3")
233
274
  end
234
275
 
276
+ # @!visibility private
235
277
  def set_location(location)
236
278
  perform_action('set_gps_coordinates',
237
279
  location[:latitude], location[:longitude])
238
280
  end
239
281
 
282
+ # @!visibility private
240
283
  def resume_app(path_or_application)
241
284
  application = parse_path_or_app_parameters(path_or_application)
242
285
 
@@ -257,6 +300,7 @@ module Calabash
257
300
  true
258
301
  end
259
302
 
303
+ # @!visibility private
260
304
  def resume_activity(package, activity)
261
305
  if package_running?(package)
262
306
  if info[:sdk_version] >= 11
@@ -273,12 +317,14 @@ module Calabash
273
317
  end
274
318
  end
275
319
 
320
+ # @!visibility private
276
321
  def app_running?(path_or_application)
277
322
  application = parse_path_or_app_parameters(path_or_application)
278
323
 
279
324
  package_running?(application.identifier)
280
325
  end
281
326
 
327
+ # @!visibility private
282
328
  def current_focus
283
329
  # Example: mFocusedApp=AppWindowToken{42c52610 token=Token{42b5d048 ActivityRecord{42a7bcc8 u0 com.example/.MainActivity t3}}}
284
330
  result = adb.shell('dumpsys window windows')
@@ -308,6 +354,7 @@ module Calabash
308
354
  raise "Unexpected output from `dumpsys window windows`"
309
355
  end
310
356
 
357
+ # @!visibility private
311
358
  def evaluate_javascript_in(query, javascript)
312
359
  parameters =
313
360
  {
@@ -699,7 +746,7 @@ module Calabash
699
746
  installed_app_md5_checksum = md5_checksum(installed_app[:path])
700
747
 
701
748
  if application.md5_checksum != installed_app_md5_checksum
702
- @logger.log("The md5 checksum has changed (#{application.md5_checksum} != #{installed_app_md5_checksum}.", :info)
749
+ @logger.log("The md5 checksum has changed (#{application.md5_checksum} != #{installed_app_md5_checksum}).", :info)
703
750
  _install_app(application)
704
751
  end
705
752
  else
@@ -819,9 +866,14 @@ module Calabash
819
866
  gesture.gestures.first.touches[0].query = query_from
820
867
  gesture.gestures.first.touches[1].query = query_to
821
868
 
822
- execute_gesture(Gestures::Gesture.with_parameters(gesture,
869
+ result = execute_gesture(Gestures::Gesture.with_parameters(gesture,
823
870
  query: query_to,
824
871
  timeout: options[:timeout]))
872
+
873
+ {
874
+ :from => Calabash::QueryResult.create(result[0], query_from),
875
+ :to => Calabash::QueryResult.create(result[1], query_to)
876
+ }
825
877
  end
826
878
 
827
879
  # @!visibility private
@@ -15,6 +15,16 @@ module Calabash
15
15
  # @!visibility private
16
16
  class InvalidJavaSDKHome < RuntimeError; end
17
17
 
18
+ # A URI that points to the embedded Calabash server in the app under test.
19
+ #
20
+ # The default value is 'http://localhost:34777'.
21
+ #
22
+ # You can control the value of this variable by setting the `CAL_ENDPOINT`
23
+ # variable.
24
+ #
25
+ # @todo Maybe rename this to CAL_SERVER_URL or CAL_SERVER?
26
+ DEVICE_ENDPOINT = URI.parse((variable('CAL_ENDPOINT') || 'http://127.0.0.1:34777'))
27
+
18
28
  private
19
29
 
20
30
  def self.set_android_dependencies(android_dependencies)
@@ -1,10 +1,14 @@
1
+ require 'date'
2
+ require 'time'
3
+
1
4
  module Calabash
2
5
  module Android
3
- # @!visibility private
6
+
7
+ # Interactions with your app that are specific to Android.
4
8
  module Interactions
5
- # Go back.
9
+ # Go back. If the keyboard is shown, it will be dismissed.
6
10
  def go_back
7
- dismiss_keyboard
11
+ Device.default.perform_action('hide_soft_keyboard')
8
12
  press_back_button
9
13
  end
10
14
 
@@ -37,6 +41,105 @@ module Calabash
37
41
  Device.default.current_focus[:package]
38
42
  end
39
43
 
44
+ # Sets the date of the first visible date picker widget.
45
+ #
46
+ # @example
47
+ # set_date('2012-04-24')
48
+ #
49
+ # @example
50
+ # date = Date.parse('3rd Feb 2012')
51
+ # set_date(date)
52
+ #
53
+ # @param [Date, String] date The date to set. If given a String,
54
+ # `Date.parse` is called on the string.
55
+ #
56
+ # @see #set_date_in
57
+ def set_date(date)
58
+ set_date_in("android.widget.DatePicker index:0", date)
59
+ end
60
+
61
+ # Sets the date of a date picker widget. If `query` matches multiple date
62
+ # pickers, the date is set for all of them.
63
+ #
64
+ # @param [String, Hash, Calabash::Query] query The query to match the
65
+ # date picker.
66
+ # @param [Date, String] date The date to set. If given a String,
67
+ # `Date.parse` is called on the string.
68
+ # @see #set_date
69
+ def set_date_in(query, date)
70
+ if date.is_a?(String)
71
+ date = Date.parse(date)
72
+ end
73
+
74
+ wait_for_view(query, timeout: Calabash::Gestures::DEFAULT_GESTURE_WAIT_TIMEOUT)
75
+ result = query(query, updateDate: [date.year, date.month, date.day])
76
+
77
+ if result.length != 1
78
+ raise "Failed to set the date of '#{query}'"
79
+ end
80
+
81
+ if result.first.is_a?(Hash) && result.first.has_key?('error')
82
+ raise result.first['error']
83
+ end
84
+
85
+ true
86
+ end
87
+
88
+ # Sets the time of the first visible time picker widget.
89
+ #
90
+ # @example
91
+ # set_time('14:42')
92
+ #
93
+ # @example
94
+ # time = Time.parse('8:30 AM')
95
+ # set_time(time)
96
+ #
97
+ # @param [Time, String] time The time to set. If given a String,
98
+ # `Time.parse` is called on the string.
99
+ #
100
+ # @see #set_time_in
101
+ def set_time(time)
102
+ set_time_in("android.widget.TimePicker index:0", time)
103
+ end
104
+
105
+ # Sets the time of a time picker widget. If `query` matches multiple time
106
+ # pickers, the time is set for all of them.
107
+ #
108
+ # @param [String, Hash, Calabash::Query] query The query to match the
109
+ # time picker.
110
+ # @param [Time, String] time The time to set. If given a String,
111
+ # `Time.parse` is called on the string.
112
+ # @see #set_time
113
+ def set_time_in(query, time)
114
+ if time.is_a?(String)
115
+ time = Time.parse(time)
116
+ end
117
+
118
+ wait_for_view(query, timeout: Calabash::Gestures::DEFAULT_GESTURE_WAIT_TIMEOUT)
119
+ result = query(query, setCurrentHour: time.hour)
120
+
121
+ if result.length != 1
122
+ raise "Failed to set the time of '#{query}'"
123
+ end
124
+
125
+ if result.first.is_a?(Hash) && result.first.has_key?('error')
126
+ raise result.first['error']
127
+ end
128
+
129
+ result = query(query, setCurrentMinute: time.min)
130
+
131
+ if result.length != 1
132
+ raise "Failed to set the time of '#{query}'"
133
+ end
134
+
135
+ if result.first.is_a?(Hash) && result.first.has_key?('error')
136
+ raise result.first['error']
137
+ end
138
+
139
+ true
140
+ end
141
+
142
+
40
143
  # @!visibility private
41
144
  def _evaluate_javascript_in(query, javascript)
42
145
  Device.default.evaluate_javascript_in(query, javascript)