testautoa 0.4.0 → 0.4.1

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.
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>