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