testautoa 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/Rakefile +7 -7
  2. data/bin/calabash-android +8 -1
  3. data/bin/calabash-android-build.rb +1 -1
  4. data/bin/calabash-android-console.rb +4 -4
  5. data/bin/calabash-android-run.rb +2 -10
  6. data/bin/testautoa +461 -0
  7. data/calabash-android.gemspec +3 -1
  8. data/features-skeleton/support/app_life_cycle_hooks.rb +0 -1
  9. data/irbrc +3 -1
  10. data/lib/calabash-android/helpers.rb +45 -17
  11. data/lib/calabash-android/lib/TestServer.apk +0 -0
  12. data/lib/calabash-android/lib/unsign.jar +0 -0
  13. data/lib/calabash-android/operations.rb +150 -66
  14. data/lib/calabash-android/steps/list_steps.rb +1 -1
  15. data/lib/calabash-android/steps/time_picker_steps.rb +1 -1
  16. data/lib/calabash-android/touch_helpers.rb +9 -0
  17. data/lib/calabash-android/version.rb +1 -1
  18. data/lib/calabash-android/wait_helpers.rb +93 -0
  19. data/test-server/AndroidManifest.xml +2 -0
  20. data/test-server/build.xml +1 -0
  21. data/test-server/instrumentation-backend/.classpath +0 -1
  22. data/test-server/instrumentation-backend/AndroidManifest.xml +1 -1
  23. data/test-server/instrumentation-backend/antlr/UIQuery.g +48 -5
  24. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Command.java +4 -3
  25. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/FranklyResult.java +95 -0
  26. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Result.java +7 -1
  27. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/HttpServer.java +14 -29
  28. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/FinishOpenedActivities.java +19 -0
  29. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GetOpenedActivities.java +31 -0
  30. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GoBackToActivity.java +67 -0
  31. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/DragCoordinates.java +28 -0
  32. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/Swipe.java +11 -5
  33. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/location/FakeGPSLocation.java +13 -10
  34. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/LeftKey.java +24 -0
  35. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/RightKey.java +24 -0
  36. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/UpKey.java +24 -0
  37. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/version/Version.java +31 -0
  38. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/CalabashChromeClient.java +131 -36
  39. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/DumpBodyHtml.java +38 -18
  40. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/DumpHtml.java +38 -16
  41. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/EnterTextByCssSelector.java +94 -66
  42. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteAsyncJavascript.java +55 -33
  43. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteJavascript.java +53 -31
  44. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptOperation.java +44 -0
  45. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/PressByCssSelector.java +52 -27
  46. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +39 -32
  47. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ScrollTo.java +56 -41
  48. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetPropertyByCssSelector.java +50 -25
  49. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java +19 -22
  50. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/CompletedFuture.java +40 -0
  51. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/InvocationOperation.java +222 -0
  52. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Operation.java +7 -0
  53. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/PropertyOperation.java +56 -0
  54. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Query.java +151 -43
  55. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQuery.tokens +19 -12
  56. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQueryResultVoid.java +22 -0
  57. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +41 -11
  58. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryLexer.java +1010 -242
  59. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryParser.java +406 -98
  60. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/BeginsWithRelation.java +45 -0
  61. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +54 -0
  62. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ContainsRelation.java +41 -0
  63. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/EndsWithRelation.java +42 -0
  64. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/LikeRelation.java +79 -0
  65. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/PartialFutureList.java +100 -0
  66. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryAST.java +1 -1
  67. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTClassName.java +54 -25
  68. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +147 -0
  69. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicateRelation.java +5 -0
  70. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +153 -89
  71. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryDirection.java +12 -2
  72. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryEvaluator.java +58 -141
  73. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +162 -7
  74. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryVisibility.java +32 -0
  75. metadata +130 -97
  76. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/Query.java +0 -24
  77. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/Touch.java +0 -44
  78. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQuery.tokens +0 -10
  79. data/test-server/instrumentation-backend/tests/sh/calaba/instrumentationbackend/query/tests/UIQueryTest.java +0 -134
@@ -38,22 +38,27 @@ def test_server_path(apk_file_path)
38
38
  "test_servers/#{checksum(apk_file_path)}_#{Calabash::Android::VERSION}.apk"
39
39
  end
40
40
 
41
+
42
+ def build_test_server_if_needed(app_path)
43
+ unless File.exist?(test_server_path(app_path))
44
+ if ARGV.include? "--no-build"
45
+ puts "No test server found for this combination of app and calabash version. Exiting!"
46
+ exit 1
47
+ else
48
+ puts "No test server found for this combination of app and calabash version. Recreating test server."
49
+ calabash_build(app_path)
50
+ end
51
+ end
52
+ end
53
+
41
54
  def resign_apk(app_path)
42
55
  Dir.mktmpdir do |tmp_dir|
43
56
  log "Resign apk"
44
57
  unsigned_path = File.join(tmp_dir, 'unsigned.apk')
45
58
  FileUtils.cp(app_path, unsigned_path)
46
59
 
47
- #Delete META-INF/*
48
- to_remove = Zip::ZipFile.foreach(unsigned_path).find_all { |e| /^META-INF\// =~ e.name}.collect &:name
60
+ `java -jar "#{File.dirname(__FILE__)}/lib/unsign.jar" "#{unsigned_path}"`
49
61
 
50
- Zip::ZipFile.open(unsigned_path) do |zip_file|
51
- to_remove.each do |x|
52
- log "Removing #{x}"
53
- zip_file.remove x
54
- end
55
- zip_file.commit
56
- end
57
62
  sign_apk(unsigned_path, app_path)
58
63
  end
59
64
  end
@@ -67,7 +72,7 @@ def sign_apk(app_path, dest_path)
67
72
  jarsigner_path = "jarsigner"
68
73
  end
69
74
 
70
- cmd = "#{jarsigner_path} -sigalg MD5withRSA -digestalg SHA1 -signedjar #{dest_path} -storepass #{keystore["keystore_password"]} -keystore \"#{File.expand_path keystore["keystore_location"]}\" #{app_path} #{keystore["keystore_alias"]}"
75
+ cmd = "#{jarsigner_path} -sigalg MD5withRSA -digestalg SHA1 -signedjar #{dest_path} -storepass #{keystore["keystore_password"]} -keystore #{keystore["keystore_location"]} #{app_path} #{keystore["keystore_alias"]}"
71
76
  log cmd
72
77
  unless system(cmd)
73
78
  puts "jarsigner command: #{cmd}"
@@ -77,19 +82,32 @@ end
77
82
 
78
83
  def read_keystore_info
79
84
  if File.exist? ".calabash_settings"
80
- JSON.parse(IO.read(".calabash_settings"))
85
+ keystore = JSON.parse(IO.read(".calabash_settings"))
86
+ keystore["keystore_location"] = '"' + File.expand_path(keystore["keystore_location"]) + '"' if keystore["keystore_location"]
87
+ keystore
81
88
  else
82
89
  {
83
- "keystore_location" => "#{ENV["HOME"]}/.android/debug.keystore",
90
+ "keystore_location" => %Q("#{File.expand_path(File.join(ENV["HOME"], "/.android/debug.keystore"))}\"),
84
91
  "keystore_password" => "android",
85
92
  "keystore_alias" => "androiddebugkey",
86
93
  }
87
94
  end
88
95
  end
89
96
 
97
+ def keytool_path
98
+ if is_windows?
99
+ "\"#{ENV["JAVA_HOME"]}/bin/keytool.exe\""
100
+ else
101
+ "keytool"
102
+ end
103
+ end
104
+
90
105
  def fingerprint_from_keystore
91
106
  keystore_info = read_keystore_info
92
- fingerprints = `keytool -v -list -alias #{keystore_info["keystore_alias"]} -keystore #{keystore_info["keystore_location"]} -storepass #{keystore_info["keystore_password"]}`
107
+ cmd = "#{keytool_path} -v -list -alias #{keystore_info["keystore_alias"]} -keystore \"#{keystore_info["keystore_location"]}\" -storepass #{keystore_info["keystore_password"]}"
108
+
109
+ log cmd
110
+ fingerprints = `#{cmd}`
93
111
  md5_fingerprint = extract_md5_fingerprint(fingerprints)
94
112
  log "MD5 fingerprint for keystore (#{keystore_info["keystore_location"]}): #{md5_fingerprint}"
95
113
  md5_fingerprint
@@ -107,7 +125,9 @@ def fingerprint_from_apk(app_path)
107
125
  raise "No RSA file found in META-INF. Cannot proceed." if rsa_files.empty?
108
126
  raise "More than one RSA file found in META-INF. Cannot proceed." if rsa_files.length > 1
109
127
 
110
- fingerprints = `keytool -v -printcert -file #{rsa_files.first}`
128
+ cmd = "#{keytool_path} -v -printcert -file #{rsa_files.first}"
129
+ log cmd
130
+ fingerprints = `#{cmd}`
111
131
  md5_fingerprint = extract_md5_fingerprint(fingerprints)
112
132
  log "MD5 fingerprint for signing cert (#{app_path}): #{md5_fingerprint}"
113
133
  md5_fingerprint
@@ -116,9 +136,17 @@ def fingerprint_from_apk(app_path)
116
136
  end
117
137
 
118
138
  def extract_md5_fingerprint(fingerprints)
119
- m = fingerprints.scan(/MD5:\s+((?:[[:xdigit:]][[:xdigit:]]:){15}[[:xdigit:]][[:xdigit:]])/)
120
- raise "No MD5 fingerprint found:\n #{fingerprints}" unless m
121
- m.last.first
139
+ log fingerprints
140
+
141
+ if is_windows?
142
+ if fingerprints.encoding.name == "CP850"
143
+ fingerprints = fingerprints.gsub("\xA0".force_encoding("CP850"),"")
144
+ end
145
+ end
146
+
147
+ m = fingerprints.scan(/MD5\s*:\s*((?:[a-fA-F\d]{2}:){15}[a-fA-F\d]{2})/).flatten
148
+ raise "No MD5 fingerprint found:\n #{fingerprints}" if m.empty?
149
+ m.first
122
150
  end
123
151
 
124
152
  def is_windows?
Binary file
@@ -6,29 +6,31 @@ require 'json'
6
6
  require 'socket'
7
7
  require 'timeout'
8
8
  require 'calabash-android/helpers'
9
+ require 'calabash-android/wait_helpers'
10
+ require 'calabash-android/touch_helpers'
11
+ require 'calabash-android/version'
9
12
  require 'retriable'
13
+ require 'cucumber'
10
14
 
11
15
 
12
16
  module Calabash module Android
13
17
 
14
18
  module Operations
15
-
19
+ include Calabash::Android::WaitHelpers
20
+ include Calabash::Android::TouchHelpers
16
21
 
17
22
  def log(message)
18
23
  $stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{message}" if (ARGV.include? "-v" or ARGV.include? "--verbose")
19
24
  end
20
25
 
21
- def take_screenshot
22
- default_device.take_screenshot
23
- end
24
-
25
26
  def macro(txt)
26
- if self.respond_to?:step
27
+ if self.respond_to?(:step)
27
28
  step(txt)
28
29
  else
29
30
  Then(txt)
30
31
  end
31
32
  end
33
+
32
34
  def default_device
33
35
  unless @default_device
34
36
  @default_device = Device.new(self, ENV["ADB_DEVICE_ARG"], ENV["TEST_SERVER_PORT"], ENV["APP_PATH"], ENV["TEST_APP_PATH"])
@@ -36,10 +38,22 @@ module Operations
36
38
  @default_device
37
39
  end
38
40
 
41
+ def set_default_device(device)
42
+ @default_device = device
43
+ end
44
+
39
45
  def performAction(action, *arguments)
40
46
  default_device.perform_action(action, *arguments)
41
47
  end
42
48
 
49
+ def reinstall_apps
50
+ default_device.reinstall_apps
51
+ end
52
+
53
+ def reinstall_test_server
54
+ default_device.reinstall_test_server
55
+ end
56
+
43
57
  def install_app(app_path)
44
58
  default_device.install_app(app_path)
45
59
  end
@@ -57,7 +71,7 @@ module Operations
57
71
  default_device.clear_app_data
58
72
  end
59
73
 
60
- def start_test_server_in_background
74
+ def start_test_server_in_background(options={})
61
75
  default_device.start_test_server_in_background()
62
76
  end
63
77
 
@@ -74,6 +88,10 @@ module Operations
74
88
  default_device.screenshot(options)
75
89
  end
76
90
 
91
+ def fail(msg="Error. Check log for details.", options={:prefix => nil, :name => nil, :label => nil})
92
+ screenshot_and_raise(msg, options)
93
+ end
94
+
77
95
  def set_gps_coordinates_from_location(location)
78
96
  default_device.set_gps_coordinates_from_location(location)
79
97
  end
@@ -82,32 +100,49 @@ module Operations
82
100
  default_device.set_gps_coordinates(latitude, longitude)
83
101
  end
84
102
 
85
- def wait_for(timeout, &block)
86
- begin
87
- Timeout::timeout(timeout) do
88
- until block.call
89
- sleep 0.3
90
- end
91
- end
92
- rescue Exception => e
93
- raise e
94
- end
95
- end
103
+ def press_back_key
104
+ default_device.press_back_key
105
+ end
106
+
107
+ #def wait_for(timeout, &block)
108
+ # value = nil
109
+ # begin
110
+ # Timeout::timeout(timeout) do
111
+ # until (value = block.call)
112
+ # sleep 0.3
113
+ # end
114
+ # end
115
+ # rescue Exception => e
116
+ # raise e
117
+ # end
118
+ # value
119
+ #end
96
120
 
97
121
  def query(uiquery, *args)
98
- if uiquery.start_with? "webView"
99
- uiquery.slice!(0, "webView".length)
100
- if uiquery =~ /(css|xpath):\s*(.*)/
101
- r = performAction("query", $1, $2)
102
- JSON.parse(r["message"])
122
+ converted_args = []
123
+ args.each do |arg|
124
+ if arg.is_a?(Hash) and arg.count == 1
125
+ converted_args << {:method_name => arg.keys.first, :arguments => [ arg.values.first ]}
103
126
  else
104
- raise "Invalid query #{uiquery}"
127
+ converted_args << arg
105
128
  end
106
- else
107
- arguments = [*args]
108
- operation = {"method_name"=>"query", "arguments" => arguments}
109
- data = {"query" => uiquery, "operation" => operation}
110
- JSON.parse(http("/map",data))
129
+ end
130
+ map(uiquery,:query,*converted_args)
131
+ end
132
+
133
+ def each_item(opts={:query => "android.widget.ListView", :post_scroll => 0.2}, &block)
134
+ uiquery = opts[:query] || "android.widget.ListView"
135
+ skip_if = opts[:skip_if] || lambda { |i| false }
136
+ stop_when = opts[:stop_when] || lambda { |i| false }
137
+ check_element_exists(uiquery)
138
+ num_items = query(opts[:query], :adapter, :count).first
139
+ num_items.times do |item|
140
+ next if skip_if.call(item)
141
+ break if stop_when.call(item)
142
+
143
+ scroll_to_row(opts[:query], item)
144
+ sleep(opts[:post_scroll]) if opts[:post_scroll] and opts[:post_scroll] > 0
145
+ yield(item)
111
146
  end
112
147
  end
113
148
 
@@ -132,10 +167,6 @@ module Operations
132
167
 
133
168
  class Device
134
169
 
135
- def make_default_device
136
- @cucumber_world.default_device = self
137
- end
138
-
139
170
  def initialize(cucumber_world, serial, server_port, app_path, test_server_path)
140
171
  @cucumber_world = cucumber_world
141
172
  @serial = serial
@@ -151,6 +182,10 @@ module Operations
151
182
  def reinstall_apps()
152
183
  uninstall_app(package_name(@app_path))
153
184
  install_app(@app_path)
185
+ reinstall_test_server()
186
+ end
187
+
188
+ def reinstall_test_server()
154
189
  uninstall_app(package_name(@test_server_path))
155
190
  install_app(@test_server_path)
156
191
  end
@@ -164,7 +199,7 @@ module Operations
164
199
  succeeded = `#{adb_command} shell pm list packages`.include?("package:#{pn}")
165
200
 
166
201
  unless succeeded
167
- Cucumber.wants_to_quit = true
202
+ ::Cucumber.wants_to_quit = true
168
203
  raise "#{pn} did not get installed. Aborting!"
169
204
  end
170
205
  end
@@ -224,25 +259,6 @@ module Operations
224
259
  end
225
260
  end
226
261
 
227
- def take_screenshot
228
- puts "take_screenshot is deprecated. Use screenshot_embed instead."
229
- path = ENV["SCREENSHOT_PATH_PREFIX"] || "results"
230
- FileUtils.mkdir_p path unless File.exist? path
231
- filename_prefix = FeatureNameMemory.feature_name.gsub(/\s+/, '_').downcase
232
- begin
233
- Timeout.timeout(30) do
234
- file_name = "#{path}/#{filename_prefix}_#{FeatureNameMemory.invocation}_#{StepCounter.step_line}.png"
235
- image = http("/screenshot")
236
- open(file_name ,"wb") { |file|
237
- file.write(image)
238
- }
239
- log "Screenshot stored in: #{file_name}!!!"
240
- end
241
- rescue Timeout::Error
242
- raise Exception, "take_screenshot timed out"
243
- end
244
- end
245
-
246
262
  def screenshot(options={:prefix => nil, :name => nil})
247
263
  prefix = options[:prefix] || ENV['SCREENSHOT_PATH'] || ""
248
264
  name = options[:name]
@@ -311,14 +327,30 @@ module Operations
311
327
  raise "Could not clear data" unless system(cmd)
312
328
  end
313
329
 
314
- def start_test_server_in_background
315
- raise "Will not start test server because of previous failures." if Cucumber.wants_to_quit
330
+ def start_test_server_in_background(options={})
331
+ raise "Will not start test server because of previous failures." if ::Cucumber.wants_to_quit
316
332
 
317
333
  if keyguard_enabled?
318
334
  wake_up
319
335
  end
320
336
 
321
- cmd = "#{adb_command} shell am instrument -e target_package #{ENV["PACKAGE_NAME"]} -e main_activity #{ENV["MAIN_ACTIVITY"]} -e class sh.calaba.instrumentationbackend.InstrumentationBackend sh.calaba.android.test/sh.calaba.instrumentationbackend.CalabashInstrumentationTestRunner"
337
+ env_options = {:target_package => options[:target_package] || package_name(@app_path),
338
+ :main_activity => options[:main_activity] || main_activity(@app_path),
339
+ :debug => options[:debug] || false,
340
+ :class => options[:class] || "sh.calaba.instrumentationbackend.InstrumentationBackend"}
341
+
342
+ cmd_arr = [adb_command, "shell am instrument"]
343
+
344
+ env_options.each_pair do |key, val|
345
+ cmd_arr << "-e"
346
+ cmd_arr << key.to_s
347
+ cmd_arr << val.to_s
348
+ end
349
+
350
+ cmd_arr << "sh.calaba.android.test/sh.calaba.instrumentationbackend.CalabashInstrumentationTestRunner"
351
+
352
+ cmd = cmd_arr.join(" ")
353
+
322
354
  log "Starting test server using:"
323
355
  log cmd
324
356
  raise "Could not execute command to start test server" unless system("#{cmd} 2>&1")
@@ -340,12 +372,37 @@ module Operations
340
372
  log "Instrumentation backend is ready!"
341
373
  end
342
374
  end
375
+ rescue Exception => e
343
376
 
344
- rescue
345
377
  msg = "Unable to make connection to Calabash Test Server at http://127.0.0.1:#{@server_port}/\n"
346
378
  msg << "Please check the logcat output for more info about what happened\n"
347
379
  raise msg
348
380
  end
381
+
382
+ log "Checking client-server version match..."
383
+ response = perform_action('version')
384
+ unless response['success']
385
+ msg = ["Unable to obtain Test Server version. "]
386
+ msg << "Please run 'reinstall_test_server' to make sure you have the correct version"
387
+ msg_s = msg.join("\n")
388
+ log(msg_s)
389
+ raise msg_s
390
+ end
391
+ unless response['message'] == Calabash::Android::VERSION
392
+
393
+ msg = ["Calabash Client and Test-server version mismatch."]
394
+ msg << "Client version #{Calabash::Android::VERSION}"
395
+ msg << "Test-server version #{response['message']}"
396
+ msg << "Expected Test-server version #{Calabash::Android::VERSION}"
397
+ msg << "\n\nSolution:\n\n"
398
+ msg << "Run 'reinstall_test_server' to make sure you have the correct version"
399
+ msg_s = msg.join("\n")
400
+ log(msg_s)
401
+ raise msg_s
402
+ end
403
+ log("Client and server versions match. Proceeding...")
404
+
405
+
349
406
  end
350
407
 
351
408
  def shutdown_test_server
@@ -369,6 +426,15 @@ module Operations
369
426
  def set_gps_coordinates(latitude, longitude)
370
427
  perform_action('set_gps_coordinates', latitude, longitude)
371
428
  end
429
+
430
+ def input_keyevent(keycode)
431
+ cmd = "#{adb_command} shell input keyevent #{keycode.to_s}"
432
+ result = `#{cmd}`
433
+ end
434
+
435
+ def press_back_key
436
+ input_keyevent(4)
437
+ end
372
438
  end
373
439
 
374
440
 
@@ -379,22 +445,24 @@ module Operations
379
445
 
380
446
  def screenshot_and_raise(msg)
381
447
  screenshot_embed
382
- sleep 5
383
448
  raise(msg)
384
449
  end
385
450
 
386
451
  def touch(uiquery,*args)
452
+ raise "Cannot touch nil" unless uiquery
453
+
387
454
  if uiquery.instance_of? String
388
455
  elements = query(uiquery, *args)
389
- raise "No elements found" if elements.empty?
456
+ raise "No elements found. Query: #{uiquery}" if elements.empty?
390
457
  element = elements.first
391
458
  else
392
459
  element = uiquery
460
+ element = element.first if element.instance_of?(Array)
393
461
  end
394
462
 
395
463
 
396
- center_x = element["frame"]["x"] + element["frame"]["width"] / 2
397
- center_y = element["frame"]["y"] + element["frame"]["height"] / 2
464
+ center_x = element["rect"]["center_x"]
465
+ center_y = element["rect"]["center_y"]
398
466
  performAction("touch_coordinate", center_x, center_y)
399
467
  end
400
468
 
@@ -436,7 +504,8 @@ module Operations
436
504
  end
437
505
 
438
506
  def scroll_to_row(uiquery,number)
439
- ni
507
+ query(uiquery, {:smoothScrollToPosition => number})
508
+ puts "TODO:detect end of scroll - use sleep for now"
440
509
  end
441
510
 
442
511
  def pinch(in_out,options={})
@@ -451,12 +520,16 @@ module Operations
451
520
  ni
452
521
  end
453
522
 
523
+ def element_does_not_exist(uiquery)
524
+ query(uiquery).empty?
525
+ end
526
+
454
527
  def element_exists(uiquery)
455
- !query(uiquery).empty?
528
+ not element_does_not_exist(uiquery)
456
529
  end
457
530
 
458
531
  def view_with_mark_exists(expected_mark)
459
- element_exists( "view marked:'#{expected_mark}'" )
532
+ element_exists( "android.view.View marked:'#{expected_mark}'" )
460
533
  end
461
534
 
462
535
  def check_element_exists( query )
@@ -505,8 +578,19 @@ module Operations
505
578
  ni
506
579
  end
507
580
 
508
- def map( query, method_name, *method_args )
509
- ni
581
+ def map(query, method_name, *method_args)
582
+ operation_map = {
583
+ :method_name => method_name,
584
+ :arguments => method_args
585
+ }
586
+ res = http("/map",
587
+ {:query => query, :operation => operation_map})
588
+ res = JSON.parse(res)
589
+ if res['outcome'] != 'SUCCESS'
590
+ screenshot_and_raise "map #{query}, #{method_name} failed because: #{res['reason']}\n#{res['details']}"
591
+ end
592
+
593
+ res['results']
510
594
  end
511
595
 
512
596
  def url_for( verb )
@@ -26,7 +26,7 @@ Then /^The "([^\"]*)" for row (\d+) should be "([^\"]*)"$/ do | view_id, row, va
26
26
 
27
27
  if( response['children'] )
28
28
  found_id = false
29
- response['children'] each do | view |
29
+ response['children'].each do | view |
30
30
  if( view['id'] == view_id )
31
31
  raise "Text is #{view['text']}, expected #{value}" unless( view['text'] == value )
32
32
  found_id = true
@@ -4,5 +4,5 @@ Given /^I set the time to "(\d\d:\d\d)" on TimePicker with index "([^\"]*)"$/ do
4
4
  end
5
5
 
6
6
  Given /^I set the "([^\"]*)" time to "(\d\d:\d\d)"$/ do |content_description, time|
7
- performAction('set_time_with_description', time, content_description)
7
+ performAction('set_time_with_description', content_description, time)
8
8
  end
@@ -0,0 +1,9 @@
1
+ module Calabash
2
+ module Android
3
+ module TouchHelpers
4
+ def tap(mark, *args)
5
+ touch("* marked:'#{mark}'", *args)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  module Calabash
2
2
  module Android
3
- VERSION = "0.4.0"
3
+ VERSION = "0.4.1"
4
4
  end
5
5
  end
@@ -0,0 +1,93 @@
1
+ module Calabash
2
+ module Android
3
+
4
+ module WaitHelpers
5
+
6
+
7
+ class WaitError < RuntimeError
8
+ end
9
+
10
+
11
+ def wait_for(options_or_timeout=
12
+ {:timeout => 10,
13
+ :retry_frequency => 0.2,
14
+ :post_timeout => 0.1,
15
+ :timeout_message => "Timed out waiting...",
16
+ :screenshot_on_error => true}, &block)
17
+ #note Hash is preferred, number acceptable for backwards compat
18
+ timeout=options_or_timeout
19
+ post_timeout=0.1
20
+ retry_frequency=0.2
21
+ timeout_message = nil
22
+ screenshot_on_error = true
23
+
24
+ if options_or_timeout.is_a?(Hash)
25
+ timeout = options_or_timeout[:timeout] || 10
26
+ retry_frequency = options_or_timeout[:retry_frequency] || 0.2
27
+ post_timeout = options_or_timeout[:post_timeout] || 0.1
28
+ timeout_message = options_or_timeout[:timeout_message]
29
+ screenshot_on_error = options_or_timeout[:screenshot_on_error] || true
30
+ end
31
+
32
+ begin
33
+ Timeout::timeout(timeout, WaitError) do
34
+ sleep(retry_frequency) until yield
35
+ end
36
+ sleep(post_timeout) if post_timeout > 0
37
+ rescue WaitError => e
38
+ handle_error_with_options(e, timeout_message, screenshot_on_error)
39
+ rescue Exception => e
40
+ handle_error_with_options(e, nil, screenshot_on_error)
41
+ end
42
+ end
43
+
44
+ def wait_poll(opts, &block)
45
+ test = opts[:until]
46
+ if test.nil?
47
+ cond = opts[:until_exists]
48
+ raise "Must provide :until or :until_exists" unless cond
49
+ test = lambda { element_exists(cond) }
50
+ end
51
+ wait_for(opts) do
52
+ if test.call()
53
+ true
54
+ else
55
+ yield
56
+ false
57
+ end
58
+ end
59
+ end
60
+
61
+ #options for wait_for apply
62
+ def wait_for_elements_exist(elements_arr, options={})
63
+ options[:timeout_message] = options[:timeout_message] || "Timeout waiting for elements: #{elements_arr.join(",")}"
64
+ wait_for(options) do
65
+ elements_arr.all? { |q| element_exists(q) }
66
+ end
67
+ end
68
+
69
+ #options for wait_for apply
70
+ def wait_for_elements_do_not_exist(elements_arr, options={})
71
+ options[:timeout_message] = options[:timeout_message] || "Timeout waiting for no elements matching: #{elements_arr.join(",")}"
72
+ wait_for(options) do
73
+ elements_arr.none? { |q| element_exists(q) }
74
+ end
75
+ end
76
+
77
+ def handle_error_with_options(ex, timeout_message, screenshot_on_error)
78
+ msg = (timeout_message || ex)
79
+ if ex
80
+ msg = "#{msg} (#{ex.class})"
81
+ end
82
+ if screenshot_on_error
83
+ screenshot_and_raise msg
84
+ else
85
+ raise msg
86
+ end
87
+ end
88
+
89
+
90
+ end
91
+ end
92
+ end
93
+
@@ -22,6 +22,8 @@
22
22
 
23
23
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
24
24
  <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
25
+ <uses-permission android:name="android.permission.ACCESS_COURSE_LOCATION"/>
26
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
25
27
  <uses-permission android:name="android.permission.INTERNET" />
26
28
 
27
29
  </manifest>
@@ -69,6 +69,7 @@
69
69
  <copy todir="${staging.dir}/assets">
70
70
  <fileset dir="${calabashjs.dir}"/>
71
71
  </copy>
72
+ <replace file="${staging.dir}/src/sh/calaba/instrumentationbackend/actions/version/Version.java" token="####VERSION####" value="${version}" failOnNoReplacements="true" />
72
73
  </target>
73
74
 
74
75
 
@@ -3,7 +3,6 @@
3
3
  <classpathentry kind="src" path="src"/>
4
4
  <classpathentry kind="src" path="gen"/>
5
5
  <classpathentry kind="src" path="assets"/>
6
- <classpathentry kind="src" path="tests"/>
7
6
  <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
8
7
  <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
9
8
  <classpathentry kind="lib" path="libs/robotium-solo-3.6.jar"/>
@@ -6,7 +6,7 @@
6
6
 
7
7
  <uses-sdk android:minSdkVersion="6" />
8
8
 
9
- <application android:label="CalabashTestServer" >
9
+ <application android:label="CalabashTestServer" android:debuggable="true">
10
10
  <uses-library android:name="android.test.runner" />
11
11
  <uses-library android:name="com.google.android.maps" android:required="false" />
12
12
  </application>