testautoa 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +7 -7
- data/bin/calabash-android +8 -1
- data/bin/calabash-android-build.rb +1 -1
- data/bin/calabash-android-console.rb +4 -4
- data/bin/calabash-android-run.rb +2 -10
- data/bin/testautoa +461 -0
- data/calabash-android.gemspec +3 -1
- data/features-skeleton/support/app_life_cycle_hooks.rb +0 -1
- data/irbrc +3 -1
- data/lib/calabash-android/helpers.rb +45 -17
- data/lib/calabash-android/lib/TestServer.apk +0 -0
- data/lib/calabash-android/lib/unsign.jar +0 -0
- data/lib/calabash-android/operations.rb +150 -66
- data/lib/calabash-android/steps/list_steps.rb +1 -1
- data/lib/calabash-android/steps/time_picker_steps.rb +1 -1
- data/lib/calabash-android/touch_helpers.rb +9 -0
- data/lib/calabash-android/version.rb +1 -1
- data/lib/calabash-android/wait_helpers.rb +93 -0
- data/test-server/AndroidManifest.xml +2 -0
- data/test-server/build.xml +1 -0
- data/test-server/instrumentation-backend/.classpath +0 -1
- data/test-server/instrumentation-backend/AndroidManifest.xml +1 -1
- data/test-server/instrumentation-backend/antlr/UIQuery.g +48 -5
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Command.java +4 -3
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/FranklyResult.java +95 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Result.java +7 -1
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/HttpServer.java +14 -29
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/FinishOpenedActivities.java +19 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GetOpenedActivities.java +31 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GoBackToActivity.java +67 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/DragCoordinates.java +28 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/Swipe.java +11 -5
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/location/FakeGPSLocation.java +13 -10
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/LeftKey.java +24 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/RightKey.java +24 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/UpKey.java +24 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/version/Version.java +31 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/CalabashChromeClient.java +131 -36
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/DumpBodyHtml.java +38 -18
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/DumpHtml.java +38 -16
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/EnterTextByCssSelector.java +94 -66
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteAsyncJavascript.java +55 -33
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteJavascript.java +53 -31
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptOperation.java +44 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/PressByCssSelector.java +52 -27
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +39 -32
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ScrollTo.java +56 -41
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetPropertyByCssSelector.java +50 -25
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java +19 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/CompletedFuture.java +40 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/InvocationOperation.java +222 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Operation.java +7 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/PropertyOperation.java +56 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Query.java +151 -43
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQuery.tokens +19 -12
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQueryResultVoid.java +22 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +41 -11
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryLexer.java +1010 -242
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryParser.java +406 -98
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/BeginsWithRelation.java +45 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +54 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ContainsRelation.java +41 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/EndsWithRelation.java +42 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/LikeRelation.java +79 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/PartialFutureList.java +100 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryAST.java +1 -1
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTClassName.java +54 -25
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +147 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicateRelation.java +5 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +153 -89
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryDirection.java +12 -2
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryEvaluator.java +58 -141
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +162 -7
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryVisibility.java +32 -0
- metadata +130 -97
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/Query.java +0 -24
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/Touch.java +0 -44
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQuery.tokens +0 -10
- 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
|
-
#
|
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
|
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"]
|
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
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
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
|
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
|
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
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
99
|
-
|
100
|
-
if
|
101
|
-
|
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
|
-
|
127
|
+
converted_args << arg
|
105
128
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
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["
|
397
|
-
center_y = element["
|
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
|
-
|
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
|
-
|
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(
|
509
|
-
|
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']
|
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',
|
7
|
+
performAction('set_time_with_description', content_description, time)
|
8
8
|
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>
|
data/test-server/build.xml
CHANGED
@@ -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>
|