calabash-android 0.5.1 → 0.5.2.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/bin/calabash-android-console.rb +24 -9
  4. data/bin/calabash-android-run.rb +4 -1
  5. data/bin/calabash-android-setup.rb +1 -1
  6. data/lib/calabash-android/deprecated_actions.map +8 -0
  7. data/lib/calabash-android/env.rb +30 -2
  8. data/lib/calabash-android/environment_helpers.rb +12 -0
  9. data/lib/calabash-android/gestures.rb +308 -0
  10. data/lib/calabash-android/helpers.rb +55 -6
  11. data/lib/calabash-android/java_keystore.rb +2 -1
  12. data/lib/calabash-android/lib/TestServer.apk +0 -0
  13. data/lib/calabash-android/operations.rb +901 -707
  14. data/lib/calabash-android/removed_actions.txt +4 -0
  15. data/lib/calabash-android/steps/date_picker_steps.rb +4 -4
  16. data/lib/calabash-android/steps/time_picker_steps.rb +3 -3
  17. data/lib/calabash-android/touch_helpers.rb +114 -18
  18. data/lib/calabash-android/version.rb +1 -1
  19. data/test-server/calabash-js/src/calabash.js +42 -1
  20. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/HttpServer.java +67 -2
  21. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/MultiTouchGesture.java +749 -0
  22. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/PressKey.java +85 -0
  23. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/HideSoftKeyboard.java +24 -2
  24. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/PressUserActionButton.java +128 -0
  25. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +8 -1
  26. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/InvocationOperation.java +56 -9
  27. metadata +10 -8
  28. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/time/SetDateByContentDescription.java +0 -33
  29. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/time/SetDateByIndex.java +0 -24
  30. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/time/SetTimeByContentDescription.java +0 -34
  31. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/time/SetTimeByIndex.java +0 -26
@@ -14,11 +14,58 @@ def package_name(app)
14
14
  end
15
15
 
16
16
  def main_activity(app)
17
- launchable_activity_line = aapt_dump(app, "launchable-activity").first
18
- raise "'launchable-activity' not found in aapt output" unless launchable_activity_line
19
- m = launchable_activity_line.match(/name='([^']+)'/)
20
- raise "Unexpected output from aapt: #{launchable_activity_line}" unless m
21
- m[1]
17
+ begin
18
+ log("Trying to find launchable activity")
19
+ launchable_activity_line = aapt_dump(app, "launchable-activity").first
20
+ raise "'launchable-activity' not found in aapt output" unless launchable_activity_line
21
+ m = launchable_activity_line.match(/name='([^']+)'/)
22
+ raise "Unexpected output from aapt: #{launchable_activity_line}" unless m
23
+ log("Found launchable activity '#{m[1]}'")
24
+ m[1]
25
+ rescue => e
26
+ log("Could not find launchable activity, trying to parse raw AndroidManifest. #{e.message}")
27
+
28
+ manifest_data = `"#{Env.tools_dir}/aapt" dump xmltree "#{app}" AndroidManifest.xml`
29
+ regex = /^\s*A:[\s*]android:name\(\w+\)\=\"android.intent.category.LAUNCHER\"/
30
+ lines = manifest_data.lines.collect(&:strip)
31
+ indicator_line = nil
32
+
33
+ lines.each_with_index do |line, index|
34
+ match = line.match(regex)
35
+
36
+ unless match.nil?
37
+ raise 'More than one launchable activity in AndroidManifest' unless indicator_line.nil?
38
+ indicator_line = index
39
+ end
40
+ end
41
+
42
+ raise 'No launchable activity found in AndroidManifest' unless indicator_line
43
+
44
+ intent_filter_found = false
45
+
46
+ (0..indicator_line).reverse_each do |index|
47
+ if intent_filter_found
48
+ match = lines[index].match(/\s*E:\s*activity-alias/)
49
+
50
+ raise 'Could not find target activity in activity alias' if match
51
+
52
+ match = lines[index].match(/^\s*A:\s*android:targetActivity\(\w*\)\=\"([^\"]+)/){$1}
53
+
54
+ if match
55
+ log("Found launchable activity '#{match}'")
56
+
57
+ return match
58
+ end
59
+ else
60
+ unless lines[index].match(/\s*E: intent-filter/).nil?
61
+ log("Read intent filter")
62
+ intent_filter_found = true
63
+ end
64
+ end
65
+ end
66
+
67
+ raise 'Could not find launchable activity'
68
+ end
22
69
  end
23
70
 
24
71
  def aapt_dump(app, key)
@@ -69,7 +116,9 @@ def unsign_apk(path)
69
116
  end
70
117
 
71
118
  def zipalign_apk(inpath, outpath)
72
- system(%Q(#{Env.zipalign_path} -f 4 "#{inpath}" "#{outpath}"))
119
+ cmd = %Q("#{Env.zipalign_path}" -f 4 "#{inpath}" "#{outpath}")
120
+ log "Zipaligning using: #{cmd}"
121
+ system(cmd)
73
122
  end
74
123
 
75
124
  def sign_apk(app_path, dest_path)
@@ -1,7 +1,7 @@
1
1
  class JavaKeystore
2
2
  attr_reader :errors, :location, :keystore_alias, :password, :fingerprint
3
3
  def initialize(location, keystore_alias, password)
4
- raise "No such file #{location}" unless File.exists?(File.expand_path(location))
4
+ raise "No such keystore file '#{location}'" unless File.exists?(File.expand_path(location))
5
5
 
6
6
  keystore_data = system_with_stdout_on_success(Env.keytool_path, '-list', '-v', '-alias', keystore_alias, '-keystore', location, '-storepass', password, '-J"-Dfile.encoding=utf-8"')
7
7
  if keystore_data.nil?
@@ -79,6 +79,7 @@ class JavaKeystore
79
79
  fail_if_key_missing(keystore, "keystore_password")
80
80
  fail_if_key_missing(keystore, "keystore_alias")
81
81
  keystore["keystore_location"] = File.expand_path(keystore["keystore_location"])
82
+ log("Keystore location specified in #{File.exist?(".calabash_settings") ? ".calabash_settings" : "calabash_settings"}.")
82
83
  JavaKeystore.new(keystore["keystore_location"], keystore["keystore_alias"], keystore["keystore_password"])
83
84
  end
84
85
 
@@ -6,7 +6,9 @@ require 'rubygems'
6
6
  require 'json'
7
7
  require 'socket'
8
8
  require 'timeout'
9
+ require 'calabash-android/gestures'
9
10
  require 'calabash-android/helpers'
11
+ require 'calabash-android/environment_helpers'
10
12
  require 'calabash-android/text_helpers'
11
13
  require 'calabash-android/touch_helpers'
12
14
  require 'calabash-android/wait_helpers'
@@ -14,526 +16,601 @@ require 'calabash-android/version'
14
16
  require 'calabash-android/env'
15
17
  require 'retriable'
16
18
  require 'cucumber'
19
+ require 'date'
20
+ require 'time'
17
21
 
18
22
 
19
23
  module Calabash module Android
20
24
 
21
- module Operations
22
- include Calabash::Android::TextHelpers
23
- include Calabash::Android::TouchHelpers
24
- include Calabash::Android::WaitHelpers
25
+ module Operations
26
+ include Calabash::Android::EnvironmentHelpers
27
+ include Calabash::Android::TextHelpers
28
+ include Calabash::Android::TouchHelpers
29
+ include Calabash::Android::WaitHelpers
25
30
 
26
- def current_activity
27
- `#{default_device.adb_command} shell dumpsys window windows`.each_line.grep(/mFocusedApp.+[\.\/]([^.\s\/\}]+)/){$1}.first
28
- end
31
+ def current_activity
32
+ `#{default_device.adb_command} shell dumpsys window windows`.each_line.grep(/mFocusedApp.+[\.\/]([^.\s\/\}]+)/){$1}.first
33
+ end
29
34
 
30
- def log(message)
31
- $stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{message}" if (ARGV.include? "-v" or ARGV.include? "--verbose")
32
- end
35
+ def log(message)
36
+ $stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{message}" if (ARGV.include? "-v" or ARGV.include? "--verbose")
37
+ end
33
38
 
34
- def macro(txt)
35
- if self.respond_to?(:step)
36
- step(txt)
37
- else
38
- Then(txt)
39
+ def macro(txt)
40
+ if self.respond_to?(:step)
41
+ step(txt)
42
+ else
43
+ Then(txt)
44
+ end
39
45
  end
40
- end
41
46
 
42
- def default_device
43
- unless @default_device
44
- @default_device = Device.new(self, ENV["ADB_DEVICE_ARG"], ENV["TEST_SERVER_PORT"], ENV["APP_PATH"], ENV["TEST_APP_PATH"])
47
+ def default_device
48
+ unless @default_device
49
+ @default_device = Device.new(self, ENV["ADB_DEVICE_ARG"], ENV["TEST_SERVER_PORT"], ENV["APP_PATH"], ENV["TEST_APP_PATH"])
50
+ end
51
+ @default_device
45
52
  end
46
- @default_device
47
- end
48
53
 
49
- def set_default_device(device)
50
- @default_device = device
51
- end
54
+ def set_default_device(device)
55
+ @default_device = device
56
+ end
52
57
 
53
- def performAction(action, *arguments)
54
- puts "Warning: The method performAction is deprecated. Please use perform_action instead."
58
+ def performAction(action, *arguments)
59
+ puts "Warning: The method performAction is deprecated. Please use perform_action instead."
55
60
 
56
- perform_action(action, *arguments)
57
- end
61
+ perform_action(action, *arguments)
62
+ end
58
63
 
59
- def perform_action(action, *arguments)
60
- @removed_actions = File.readlines(File.join(File.dirname(__FILE__), 'removed_actions.txt')) unless @removed_actions
61
- @removed_actions.map! &:chomp
64
+ def perform_action(action, *arguments)
65
+ if removed_actions.include?(action)
66
+ puts "\e[31mError: The action '#{action}' was removed in calabash-android 0.5\e[0m"
67
+ puts 'Solutions that do not require the removed action can be found on:'
68
+ puts "\e[36mhttps://github.com/calabash/calabash-android/blob/master/migrating_to_calabash_0.5.md\##{action}\e[0m"
69
+ elsif deprecated_actions.has_key?(action)
70
+ puts "\e[31mWarning: The action '#{action}' is deprecated\e[0m"
71
+ puts "\e[32mUse '#{deprecated_actions[action]}' instead\e[0m"
72
+ end
62
73
 
63
- if @removed_actions.include?(action)
64
- puts "\e[31mError: The action '#{action}' was removed in calabash-android 0.5\e[0m"
65
- puts 'Solutions that do not require the removed action can be found on:'
66
- puts "\e[36mhttps://github.com/calabash/calabash-android/blob/master/migrating_to_calabash_0.5.md\##{action}\e[0m"
74
+ default_device.perform_action(action, *arguments)
67
75
  end
68
-
69
- default_device.perform_action(action, *arguments)
70
- end
71
76
 
72
- def reinstall_apps
73
- default_device.reinstall_apps
74
- end
77
+ def removed_actions
78
+ @removed_actions ||= File.readlines(File.join(File.dirname(__FILE__), 'removed_actions.txt')).map(&:chomp)
79
+ end
75
80
 
76
- def reinstall_test_server
77
- default_device.reinstall_test_server
78
- end
81
+ def deprecated_actions
82
+ @deprecated_actions ||= Hash[
83
+ *File.readlines(File.join(File.dirname(__FILE__), 'deprecated_actions.map')).map{|e| e.chomp.split(',', 2)}.flatten
84
+ ]
85
+ end
79
86
 
80
- def install_app(app_path)
81
- default_device.install_app(app_path)
82
- end
87
+ def reinstall_apps
88
+ default_device.reinstall_apps
89
+ end
83
90
 
84
- def update_app(app_path)
85
- default_device.update_app(app_path)
86
- end
91
+ def reinstall_test_server
92
+ default_device.reinstall_test_server
93
+ end
87
94
 
88
- def uninstall_apps
89
- default_device.uninstall_app(package_name(default_device.test_server_path))
90
- default_device.uninstall_app(package_name(default_device.app_path))
91
- end
95
+ def install_app(app_path)
96
+ default_device.install_app(app_path)
97
+ end
92
98
 
93
- def wake_up
94
- default_device.wake_up()
95
- end
99
+ def update_app(app_path)
100
+ default_device.update_app(app_path)
101
+ end
96
102
 
97
- def clear_app_data
98
- default_device.clear_app_data
99
- end
103
+ def uninstall_apps
104
+ default_device.uninstall_app(package_name(default_device.test_server_path))
105
+ default_device.uninstall_app(package_name(default_device.app_path))
106
+ end
100
107
 
101
- def pull(remote, local)
102
- default_device.pull(remote, local)
103
- end
108
+ def wake_up
109
+ default_device.wake_up()
110
+ end
104
111
 
105
- def push(local, remote)
106
- default_device.push(local, remote)
107
- end
112
+ def clear_app_data
113
+ default_device.clear_app_data
114
+ end
108
115
 
109
- def start_test_server_in_background(options={})
110
- default_device.start_test_server_in_background(options)
111
- end
116
+ def pull(remote, local)
117
+ default_device.pull(remote, local)
118
+ end
112
119
 
113
- def shutdown_test_server
114
- default_device.shutdown_test_server
115
- end
120
+ def push(local, remote)
121
+ default_device.push(local, remote)
122
+ end
116
123
 
117
- def screenshot_embed(options={:prefix => nil, :name => nil, :label => nil})
118
- path = default_device.screenshot(options)
119
- embed(path, "image/png", options[:label] || File.basename(path))
120
- end
124
+ def start_test_server_in_background(options={})
125
+ default_device.start_test_server_in_background(options)
126
+ end
121
127
 
122
- def screenshot(options={:prefix => nil, :name => nil})
123
- default_device.screenshot(options)
124
- end
128
+ def shutdown_test_server
129
+ default_device.shutdown_test_server
130
+ end
125
131
 
126
- def fail(msg="Error. Check log for details.", options={:prefix => nil, :name => nil, :label => nil})
127
- screenshot_and_raise(msg, options)
128
- end
132
+ def screenshot_embed(options={:prefix => nil, :name => nil, :label => nil})
133
+ path = default_device.screenshot(options)
134
+ embed(path, "image/png", options[:label] || File.basename(path))
135
+ end
129
136
 
130
- def set_gps_coordinates_from_location(location)
131
- default_device.set_gps_coordinates_from_location(location)
132
- end
137
+ def screenshot(options={:prefix => nil, :name => nil})
138
+ default_device.screenshot(options)
139
+ end
133
140
 
134
- def set_gps_coordinates(latitude, longitude)
135
- default_device.set_gps_coordinates(latitude, longitude)
136
- end
141
+ def client_version
142
+ default_device.client_version
143
+ end
137
144
 
138
- def get_preferences(name)
139
- default_device.get_preferences(name)
140
- end
145
+ def server_version
146
+ default_device.server_version
147
+ end
141
148
 
142
- def set_preferences(name, hash)
143
- default_device.set_preferences(name, hash)
144
- end
149
+ def fail(msg="Error. Check log for details.", options={:prefix => nil, :name => nil, :label => nil})
150
+ screenshot_and_raise(msg, options)
151
+ end
145
152
 
146
- def clear_preferences(name)
147
- default_device.clear_preferences(name)
148
- end
153
+ def set_gps_coordinates_from_location(location)
154
+ default_device.set_gps_coordinates_from_location(location)
155
+ end
149
156
 
150
- def query(uiquery, *args)
151
- converted_args = []
152
- args.each do |arg|
153
- if arg.is_a?(Hash) and arg.count == 1
154
- if arg.values.is_a?(Array) && arg.values.count == 1
155
- values = arg.values.flatten
156
- else
157
- values = [arg.values]
158
- end
157
+ def set_gps_coordinates(latitude, longitude)
158
+ default_device.set_gps_coordinates(latitude, longitude)
159
+ end
159
160
 
160
- converted_args << {:method_name => arg.keys.first, :arguments => values}
161
- else
162
- converted_args << arg
163
- end
161
+ def get_preferences(name)
162
+ default_device.get_preferences(name)
164
163
  end
165
- map(uiquery,:query,*converted_args)
166
- end
167
164
 
168
- def flash(query_string)
169
- map(query_string, :flash)
170
- end
165
+ def set_preferences(name, hash)
166
+ default_device.set_preferences(name, hash)
167
+ end
171
168
 
172
- def each_item(opts={:query => "android.widget.ListView", :post_scroll => 0.2}, &block)
173
- uiquery = opts[:query] || "android.widget.ListView"
174
- skip_if = opts[:skip_if] || lambda { |i| false }
175
- stop_when = opts[:stop_when] || lambda { |i| false }
176
- check_element_exists(uiquery)
177
- num_items = query(opts[:query], :adapter, :count).first
178
- num_items.times do |item|
179
- next if skip_if.call(item)
180
- break if stop_when.call(item)
169
+ def clear_preferences(name)
170
+ default_device.clear_preferences(name)
171
+ end
181
172
 
182
- scroll_to_row(opts[:query], item)
183
- sleep(opts[:post_scroll]) if opts[:post_scroll] and opts[:post_scroll] > 0
184
- yield(item)
173
+ def query(uiquery, *args)
174
+ converted_args = []
175
+ args.each do |arg|
176
+ if arg.is_a?(Hash) and arg.count == 1
177
+ if arg.values.is_a?(Array) && arg.values.count == 1
178
+ values = arg.values.flatten
179
+ else
180
+ values = [arg.values]
181
+ end
182
+
183
+ converted_args << {:method_name => arg.keys.first, :arguments => values}
184
+ else
185
+ converted_args << arg
186
+ end
187
+ end
188
+ map(uiquery,:query,*converted_args)
185
189
  end
186
- end
187
190
 
188
- def ni
189
- raise "Not yet implemented."
190
- end
191
+ def flash(query_string)
192
+ map(query_string, :flash)
193
+ end
191
194
 
192
- ###
195
+ def each_item(opts={:query => "android.widget.ListView", :post_scroll => 0.2}, &block)
196
+ uiquery = opts[:query] || "android.widget.ListView"
197
+ skip_if = opts[:skip_if] || lambda { |i| false }
198
+ stop_when = opts[:stop_when] || lambda { |i| false }
199
+ check_element_exists(uiquery)
200
+ num_items = query(opts[:query], :adapter, :count).first
201
+ num_items.times do |item|
202
+ next if skip_if.call(item)
203
+ break if stop_when.call(item)
204
+
205
+ scroll_to_row(opts[:query], item)
206
+ sleep(opts[:post_scroll]) if opts[:post_scroll] and opts[:post_scroll] > 0
207
+ yield(item)
208
+ end
209
+ end
193
210
 
194
- ### simple page object helper
211
+ def set_date(query_string, year_or_datestring, month=nil, day=nil)
212
+ wait_for_element_exists(query_string)
195
213
 
196
- def page(clz, *args)
197
- clz.new(self, *args)
198
- end
214
+ if month.nil? && day.nil? && year_or_datestring.is_a?(String)
215
+ date = Date.parse(year_or_datestring)
216
+ set_date(query_string, date.year, date.month, date.day)
217
+ else
218
+ year = year_or_datestring
219
+ query(query_string, updateDate: [year, month-1, day])
220
+ end
221
+ end
199
222
 
200
- ###
223
+ def set_time(query_string, hour_or_timestring, minute=nil)
224
+ wait_for_element_exists(query_string)
201
225
 
202
- ### app life cycle
203
- def connect_to_test_server
204
- puts "Explicit calls to connect_to_test_server should be removed."
205
- puts "Please take a look in your hooks file for calls to this methods."
206
- puts "(Hooks are stored in features/support)"
207
- end
226
+ if minute.nil? && hour_or_timestring.is_a?(String)
227
+ time = Time.parse(hour_or_timestring)
228
+ set_time(query_string, time.hour, time.min)
229
+ else
230
+ hour = hour_or_timestring
231
+ query(query_string, setCurrentHour: hour)
232
+ query(query_string, setCurrentMinute: minute)
233
+ end
234
+ end
208
235
 
209
- def disconnect_from_test_server
210
- puts "Explicit calls to disconnect_from_test_server should be removed."
211
- puts "Please take a look in your hooks file for calls to this methods."
212
- puts "(Hooks are stored in features/support)"
213
- end
236
+ def classes(query_string, *args)
237
+ query(query_string, :class, *args)
238
+ end
214
239
 
215
- class Device
216
- attr_reader :app_path, :test_server_path, :serial, :server_port, :test_server_port
240
+ def ni
241
+ raise "Not yet implemented."
242
+ end
217
243
 
218
- def initialize(cucumber_world, serial, server_port, app_path, test_server_path, test_server_port = 7102)
244
+ ###
219
245
 
220
- @cucumber_world = cucumber_world
221
- @serial = serial || default_serial
222
- @server_port = server_port || default_server_port
223
- @app_path = app_path
224
- @test_server_path = test_server_path
225
- @test_server_port = test_server_port
246
+ ### simple page object helper
226
247
 
227
- forward_cmd = "#{adb_command} forward tcp:#{@server_port} tcp:#{@test_server_port}"
228
- log forward_cmd
229
- log `#{forward_cmd}`
248
+ def page(clz, *args)
249
+ clz.new(self, *args)
230
250
  end
231
251
 
232
- def reinstall_apps()
233
- uninstall_app(package_name(@app_path))
234
- install_app(@app_path)
235
- reinstall_test_server()
252
+ ###
253
+
254
+ ### app life cycle
255
+ def connect_to_test_server
256
+ puts "Explicit calls to connect_to_test_server should be removed."
257
+ puts "Please take a look in your hooks file for calls to this methods."
258
+ puts "(Hooks are stored in features/support)"
236
259
  end
237
260
 
238
- def reinstall_test_server()
239
- uninstall_app(package_name(@test_server_path))
240
- install_app(@test_server_path)
261
+ def disconnect_from_test_server
262
+ puts "Explicit calls to disconnect_from_test_server should be removed."
263
+ puts "Please take a look in your hooks file for calls to this methods."
264
+ puts "(Hooks are stored in features/support)"
241
265
  end
242
266
 
243
- def install_app(app_path)
244
- cmd = "#{adb_command} install \"#{app_path}\""
245
- log "Installing: #{app_path}"
246
- result = `#{cmd}`
247
- log result
248
- pn = package_name(app_path)
249
- succeeded = `#{adb_command} shell pm list packages`.include?("package:#{pn}")
267
+ class Device
268
+ attr_reader :app_path, :test_server_path, :serial, :server_port, :test_server_port
250
269
 
251
- unless succeeded
252
- ::Cucumber.wants_to_quit = true
253
- raise "#{pn} did not get installed. Aborting!"
254
- end
255
- end
270
+ def initialize(cucumber_world, serial, server_port, app_path, test_server_path, test_server_port = 7102)
256
271
 
257
- def update_app(app_path)
258
- cmd = "#{adb_command} install -r \"#{app_path}\""
259
- log "Updating: #{app_path}"
260
- result = `#{cmd}`
261
- log "result: #{result}"
262
- succeeded = result.include?("Success")
272
+ @cucumber_world = cucumber_world
273
+ @serial = serial || default_serial
274
+ @server_port = server_port || default_server_port
275
+ @app_path = app_path
276
+ @test_server_path = test_server_path
277
+ @test_server_port = test_server_port
263
278
 
264
- unless succeeded
265
- ::Cucumber.wants_to_quit = true
266
- raise "#{pn} did not get updated. Aborting!"
279
+ forward_cmd = "#{adb_command} forward tcp:#{@server_port} tcp:#{@test_server_port}"
280
+ log forward_cmd
281
+ log `#{forward_cmd}`
267
282
  end
268
- end
269
-
270
- def uninstall_app(package_name)
271
- log "Uninstalling: #{package_name}"
272
- log `#{adb_command} uninstall #{package_name}`
273
- end
274
283
 
275
- def app_running?
276
- begin
277
- http("/ping") == "pong"
278
- rescue
279
- false
284
+ def reinstall_apps
285
+ uninstall_app(package_name(@app_path))
286
+ uninstall_app(package_name(@test_server_path))
287
+ install_app(@app_path)
288
+ install_app(@test_server_path)
280
289
  end
281
- end
282
290
 
283
- def keyguard_enabled?
284
- dumpsys = `#{adb_command} shell dumpsys window windows`
285
- #If a line containing mCurrentFocus and Keyguard exists the keyguard is enabled
286
- dumpsys.lines.any? { |l| l.include?("mCurrentFocus") and l.include?("Keyguard")}
287
- end
291
+ def reinstall_test_server
292
+ uninstall_app(package_name(@test_server_path))
293
+ install_app(@test_server_path)
294
+ end
288
295
 
289
- def perform_action(action, *arguments)
290
- log "Action: #{action} - Params: #{arguments.join(', ')}"
296
+ def install_app(app_path)
297
+ cmd = "#{adb_command} install \"#{app_path}\""
298
+ log "Installing: #{app_path}"
299
+ result = `#{cmd}`
300
+ log result
301
+ pn = package_name(app_path)
302
+ succeeded = `#{adb_command} shell pm list packages`.lines.map{|line| line.chomp.sub("package:", "")}.include?(pn)
303
+
304
+ unless succeeded
305
+ ::Cucumber.wants_to_quit = true
306
+ raise "#{pn} did not get installed. Reason: '#{result.lines.last.chomp}'. Aborting!"
307
+ end
308
+ end
291
309
 
292
- params = {"command" => action, "arguments" => arguments}
310
+ def update_app(app_path)
311
+ cmd = "#{adb_command} install -r \"#{app_path}\""
312
+ log "Updating: #{app_path}"
313
+ result = `#{cmd}`
314
+ log "result: #{result}"
315
+ succeeded = result.include?("Success")
293
316
 
294
- Timeout.timeout(300) do
295
- begin
296
- result = http("/", params, {:read_timeout => 350})
297
- rescue Exception => e
298
- log "Error communicating with test server: #{e}"
299
- raise e
300
- end
301
- log "Result:'" + result.strip + "'"
302
- raise "Empty result from TestServer" if result.chomp.empty?
303
- result = JSON.parse(result)
304
- if not result["success"] then
305
- raise "Action '#{action}' unsuccessful: #{result["message"]}"
317
+ unless succeeded
318
+ ::Cucumber.wants_to_quit = true
319
+ raise "#{pn} did not get updated. Aborting!"
306
320
  end
307
- result
308
321
  end
309
- rescue Timeout::Error
310
- raise Exception, "Step timed out"
311
- end
312
322
 
313
- def http(path, data = {}, options = {})
314
- begin
323
+ def uninstall_app(package_name)
324
+ log "Uninstalling: #{package_name}"
325
+ log `#{adb_command} uninstall #{package_name}`
315
326
 
316
- configure_http(@http, options)
317
- make_http_request(
318
- :method => :post,
319
- :body => data.to_json,
320
- :uri => url_for(path),
321
- :header => {"Content-Type" => "application/json;charset=utf-8"})
327
+ succeeded = !(`#{adb_command} shell pm list packages`.lines.map{|line| line.chomp.sub("package:", "")}.include?(package_name))
322
328
 
323
- rescue HTTPClient::TimeoutError,
324
- HTTPClient::KeepAliveDisconnected,
325
- Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED,
326
- Errno::ETIMEDOUT => e
327
- log "It looks like your app is no longer running. \nIt could be because of a crash or because your test script shut it down."
328
- raise e
329
+ unless succeeded
330
+ ::Cucumber.wants_to_quit = true
331
+ raise "#{package_name} was not uninstalled. Aborting!"
332
+ end
329
333
  end
330
- end
331
334
 
332
- def set_http(http)
333
- @http = http
334
- end
335
+ def app_running?
336
+ begin
337
+ http("/ping") == "pong"
338
+ rescue
339
+ false
340
+ end
341
+ end
335
342
 
336
- def url_for(method)
337
- url = URI.parse(ENV['DEVICE_ENDPOINT']|| "http://127.0.0.1:#{@server_port}")
338
- path = url.path
339
- if path.end_with? "/"
340
- path = "#{path}#{method}"
341
- else
342
- path = "#{path}/#{method}"
343
+ def keyguard_enabled?
344
+ dumpsys = `#{adb_command} shell dumpsys window windows`
345
+ #If a line containing mCurrentFocus and Keyguard exists the keyguard is enabled
346
+ dumpsys.lines.any? { |l| l.include?("mCurrentFocus") and l.include?("Keyguard")}
343
347
  end
344
- url.path = path
345
- url
346
- end
347
348
 
349
+ def perform_action(action, *arguments)
350
+ log "Action: #{action} - Params: #{arguments.join(', ')}"
351
+
352
+ params = {"command" => action, "arguments" => arguments}
353
+
354
+ Timeout.timeout(300) do
355
+ begin
356
+ result = http("/", params, {:read_timeout => 350})
357
+ rescue Exception => e
358
+ log "Error communicating with test server: #{e}"
359
+ raise e
360
+ end
361
+ log "Result:'" + result.strip + "'"
362
+ raise "Empty result from TestServer" if result.chomp.empty?
363
+ result = JSON.parse(result)
364
+ if not result["success"] then
365
+ raise "Action '#{action}' unsuccessful: #{result["message"]}"
366
+ end
367
+ result
368
+ end
369
+ rescue Timeout::Error
370
+ raise Exception, "Step timed out"
371
+ end
348
372
 
373
+ def http(path, data = {}, options = {})
374
+ begin
349
375
 
350
- def make_http_request(options)
351
- begin
352
- unless @http
353
- @http = init_request(options)
376
+ configure_http(@http, options)
377
+ make_http_request(
378
+ :method => :post,
379
+ :body => data.to_json,
380
+ :uri => url_for(path),
381
+ :header => {"Content-Type" => "application/json;charset=utf-8"})
382
+
383
+ rescue HTTPClient::TimeoutError,
384
+ HTTPClient::KeepAliveDisconnected,
385
+ Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED,
386
+ Errno::ETIMEDOUT => e
387
+ log "It looks like your app is no longer running. \nIt could be because of a crash or because your test script shut it down."
388
+ raise e
354
389
  end
355
- header = options[:header] || {}
356
- header["Content-Type"] = "application/json;charset=utf-8"
357
- options[:header] = header
390
+ end
358
391
 
392
+ def set_http(http)
393
+ @http = http
394
+ end
359
395
 
360
- response = if options[:method] == :post
361
- @http.post(options[:uri], options)
396
+ def url_for(method)
397
+ url = URI.parse(ENV['DEVICE_ENDPOINT']|| "http://127.0.0.1:#{@server_port}")
398
+ path = url.path
399
+ if path.end_with? "/"
400
+ path = "#{path}#{method}"
362
401
  else
363
- @http.get(options[:uri], options)
402
+ path = "#{path}/#{method}"
364
403
  end
365
- raise Errno::ECONNREFUSED if response.status_code == 502
366
- response.body
367
- rescue Exception => e
368
- if @http
369
- @http.reset_all
370
- @http=nil
404
+ url.path = path
405
+ url
406
+ end
407
+
408
+
409
+
410
+ def make_http_request(options)
411
+ begin
412
+ unless @http
413
+ @http = init_request(options)
414
+ end
415
+ header = options[:header] || {}
416
+ header["Content-Type"] = "application/json;charset=utf-8"
417
+ options[:header] = header
418
+
419
+
420
+ response = if options[:method] == :post
421
+ @http.post(options[:uri], options)
422
+ else
423
+ @http.get(options[:uri], options)
424
+ end
425
+ raise Errno::ECONNREFUSED if response.status_code == 502
426
+ response.body
427
+ rescue Exception => e
428
+ if @http
429
+ @http.reset_all
430
+ @http=nil
431
+ end
432
+ raise e
371
433
  end
372
- raise e
373
434
  end
374
- end
375
435
 
376
- def init_request(options)
377
- http = HTTPClient.new
378
- configure_http(http, options)
379
- end
436
+ def init_request(options)
437
+ http = HTTPClient.new
438
+ configure_http(http, options)
439
+ end
380
440
 
381
- def configure_http(http, options)
382
- return unless http
383
- http.connect_timeout = options[:open_timeout] || 15
384
- http.send_timeout = options[:send_timeout] || 15
385
- http.receive_timeout = options[:read_timeout] || 15
386
- if options.has_key?(:debug) && options[:debug]
387
- http.debug_dev= $stdout
388
- else
389
- if ENV['DEBUG_HTTP'] and (ENV['DEBUG_HTTP'] != '0')
390
- http.debug_dev = $stdout
441
+ def configure_http(http, options)
442
+ return unless http
443
+ http.connect_timeout = options[:open_timeout] || 15
444
+ http.send_timeout = options[:send_timeout] || 15
445
+ http.receive_timeout = options[:read_timeout] || 15
446
+ if options.has_key?(:debug) && options[:debug]
447
+ http.debug_dev= $stdout
391
448
  else
392
- http.debug_dev= nil
449
+ if ENV['DEBUG_HTTP'] and (ENV['DEBUG_HTTP'] != '0')
450
+ http.debug_dev = $stdout
451
+ else
452
+ http.debug_dev= nil
453
+ end
393
454
  end
455
+ http
394
456
  end
395
- http
396
- end
397
457
 
398
- def screenshot(options={:prefix => nil, :name => nil})
399
- prefix = options[:prefix] || ENV['SCREENSHOT_PATH'] || ""
400
- name = options[:name]
458
+ def screenshot(options={:prefix => nil, :name => nil})
459
+ prefix = options[:prefix] || ENV['SCREENSHOT_PATH'] || ""
460
+ name = options[:name]
401
461
 
402
- if name.nil?
403
- name = "screenshot"
404
- else
405
- if File.extname(name).downcase == ".png"
406
- name = name.split(".png")[0]
462
+ if name.nil?
463
+ name = "screenshot"
464
+ else
465
+ if File.extname(name).downcase == ".png"
466
+ name = name.split(".png")[0]
467
+ end
468
+ end
469
+
470
+ @@screenshot_count ||= 0
471
+ path = "#{prefix}#{name}_#{@@screenshot_count}.png"
472
+
473
+ if ENV["SCREENSHOT_VIA_USB"] == "false"
474
+ begin
475
+ res = http("/screenshot")
476
+ rescue EOFError
477
+ raise "Could not take screenshot. App is most likely not running anymore."
478
+ end
479
+ File.open(path, 'wb') do |f|
480
+ f.write res
481
+ end
482
+ else
483
+ screenshot_cmd = "java -jar #{File.join(File.dirname(__FILE__), 'lib', 'screenshotTaker.jar')} #{serial} \"#{path}\""
484
+ log screenshot_cmd
485
+ raise "Could not take screenshot" unless system(screenshot_cmd)
407
486
  end
487
+
488
+ @@screenshot_count += 1
489
+ path
408
490
  end
409
491
 
410
- @@screenshot_count ||= 0
411
- path = "#{prefix}#{name}_#{@@screenshot_count}.png"
492
+ def client_version
493
+ Calabash::Android::VERSION
494
+ end
412
495
 
413
- if ENV["SCREENSHOT_VIA_USB"] == "false"
496
+ def server_version
414
497
  begin
415
- res = http("/screenshot")
416
- rescue EOFError
417
- raise "Could not take screenshot. App is most likely not running anymore."
498
+ response = perform_action('version')
499
+ raise 'Invalid response' unless response['success']
500
+ rescue => e
501
+ log("Could not contact server")
502
+ log(e && e.backtrace && e.backtrace.join("\n"))
503
+ raise "The server did not respond. Make sure the server is running."
418
504
  end
419
- File.open(path, 'wb') do |f|
420
- f.write res
421
- end
422
- else
423
- screenshot_cmd = "java -jar #{File.join(File.dirname(__FILE__), 'lib', 'screenshotTaker.jar')} #{serial} \"#{path}\""
424
- log screenshot_cmd
425
- raise "Could not take screenshot" unless system(screenshot_cmd)
426
- end
427
505
 
428
- @@screenshot_count += 1
429
- path
430
- end
506
+ response['message']
507
+ end
431
508
 
432
- def adb_command
433
- "#{Env.adb_path} -s #{serial}"
434
- end
509
+ def adb_command
510
+ "#{Env.adb_path} -s #{serial}"
511
+ end
435
512
 
436
- def default_serial
437
- devices = connected_devices
438
- log "connected_devices: #{devices}"
439
- raise "No connected devices" if devices.empty?
440
- raise "More than one device connected. Specify device serial using ADB_DEVICE_ARG" if devices.length > 1
441
- devices.first
442
- end
513
+ def default_serial
514
+ devices = connected_devices
515
+ log "connected_devices: #{devices}"
516
+ raise "No connected devices" if devices.empty?
517
+ raise "More than one device connected. Specify device serial using ADB_DEVICE_ARG" if devices.length > 1
518
+ devices.first
519
+ end
443
520
 
444
- def default_server_port
445
- require 'yaml'
446
- File.open(File.expand_path(server_port_configuration), File::RDWR|File::CREAT) do |f|
447
- f.flock(File::LOCK_EX)
448
- state = YAML::load(f) || {}
449
- ports = state['server_ports'] ||= {}
450
- return ports[serial] if ports.has_key?(serial)
521
+ def default_server_port
522
+ require 'yaml'
523
+ File.open(File.expand_path(server_port_configuration), File::RDWR|File::CREAT) do |f|
524
+ f.flock(File::LOCK_EX)
525
+ state = YAML::load(f) || {}
526
+ ports = state['server_ports'] ||= {}
527
+ return ports[serial] if ports.has_key?(serial)
451
528
 
452
- port = 34777
453
- port += 1 while ports.has_value?(port)
454
- ports[serial] = port
529
+ port = 34777
530
+ port += 1 while ports.has_value?(port)
531
+ ports[serial] = port
455
532
 
456
- f.rewind
457
- f.write(YAML::dump(state))
458
- f.truncate(f.pos)
533
+ f.rewind
534
+ f.write(YAML::dump(state))
535
+ f.truncate(f.pos)
459
536
 
460
- log "Persistently allocated port #{port} to #{serial}"
461
- return port
537
+ log "Persistently allocated port #{port} to #{serial}"
538
+ return port
539
+ end
462
540
  end
463
- end
464
541
 
465
- def server_port_configuration
466
- File.expand_path(ENV['CALABASH_SERVER_PORTS'] || "~/.calabash.yaml")
467
- end
542
+ def server_port_configuration
543
+ File.expand_path(ENV['CALABASH_SERVER_PORTS'] || "~/.calabash.yaml")
544
+ end
468
545
 
469
- def connected_devices
470
- lines = `#{Env.adb_path} devices`.split("\n")
471
- start_index = lines.index{ |x| x =~ /List of devices attached/ } + 1
472
- lines[start_index..-1].collect { |l| l.split("\t").first }
473
- end
546
+ def connected_devices
547
+ lines = `#{Env.adb_path} devices`.split("\n")
548
+ start_index = lines.index{ |x| x =~ /List of devices attached/ } + 1
549
+ lines[start_index..-1].collect { |l| l.split("\t").first }
550
+ end
474
551
 
475
- def wake_up
476
- wake_up_cmd = "#{adb_command} shell am start -a android.intent.action.MAIN -n #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.WakeUp"
477
- log "Waking up device using:"
478
- log wake_up_cmd
479
- raise "Could not wake up the device" unless system(wake_up_cmd)
552
+ def wake_up
553
+ wake_up_cmd = "#{adb_command} shell am start -a android.intent.action.MAIN -n #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.WakeUp"
554
+ log "Waking up device using:"
555
+ log wake_up_cmd
556
+ raise "Could not wake up the device" unless system(wake_up_cmd)
480
557
 
481
- retriable :tries => 10, :interval => 1 do
482
- raise "Could not remove the keyguard" if keyguard_enabled?
558
+ retriable :tries => 10, :interval => 1 do
559
+ raise "Could not remove the keyguard" if keyguard_enabled?
560
+ end
483
561
  end
484
- end
485
562
 
486
- def clear_app_data
487
- cmd = "#{adb_command} shell am instrument #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.ClearAppData"
488
- raise "Could not clear data" unless system(cmd)
489
- end
563
+ def clear_app_data
564
+ cmd = "#{adb_command} shell am instrument #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.ClearAppData"
565
+ raise "Could not clear data" unless system(cmd)
566
+ end
490
567
 
491
- def pull(remote, local)
492
- cmd = "#{adb_command} pull #{remote} #{local}"
493
- raise "Could not pull #{remote} to #{local}" unless system(cmd)
494
- end
568
+ def pull(remote, local)
569
+ cmd = "#{adb_command} pull #{remote} #{local}"
570
+ raise "Could not pull #{remote} to #{local}" unless system(cmd)
571
+ end
495
572
 
496
- def push(local, remote)
497
- cmd = "#{adb_command} push #{local} #{remote}"
498
- raise "Could not push #{local} to #{remote}" unless system(cmd)
499
- end
573
+ def push(local, remote)
574
+ cmd = "#{adb_command} push #{local} #{remote}"
575
+ raise "Could not push #{local} to #{remote}" unless system(cmd)
576
+ end
500
577
 
501
- def start_test_server_in_background(options={})
502
- raise "Will not start test server because of previous failures." if ::Cucumber.wants_to_quit
578
+ def start_test_server_in_background(options={})
579
+ raise "Will not start test server because of previous failures." if ::Cucumber.wants_to_quit
503
580
 
504
- if keyguard_enabled?
505
- wake_up
506
- end
581
+ if keyguard_enabled?
582
+ wake_up
583
+ end
507
584
 
508
- env_options = {:target_package => package_name(@app_path),
509
- :main_activity => main_activity(@app_path),
510
- :test_server_port => @test_server_port,
511
- :class => "sh.calaba.instrumentationbackend.InstrumentationBackend"}
585
+ env_options = options
512
586
 
513
- env_options = env_options.merge(options)
587
+ env_options[:target_package] ||= package_name(@app_path)
588
+ env_options[:main_activity] ||= main_activity(@app_path)
589
+ env_options[:test_server_port] ||= @test_server_port
590
+ env_options[:class] ||= "sh.calaba.instrumentationbackend.InstrumentationBackend"
514
591
 
515
- cmd_arr = [adb_command, "shell am instrument"]
592
+ cmd_arr = [adb_command, "shell am instrument"]
516
593
 
517
- env_options.each_pair do |key, val|
518
- cmd_arr << "-e"
519
- cmd_arr << key.to_s
520
- cmd_arr << val.to_s
521
- end
594
+ env_options.each_pair do |key, val|
595
+ cmd_arr << "-e"
596
+ cmd_arr << key.to_s
597
+ cmd_arr << val.to_s
598
+ end
522
599
 
523
- cmd_arr << "#{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.CalabashInstrumentationTestRunner"
600
+ cmd_arr << "#{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.CalabashInstrumentationTestRunner"
524
601
 
525
- cmd = cmd_arr.join(" ")
602
+ cmd = cmd_arr.join(" ")
526
603
 
527
- log "Starting test server using:"
528
- log cmd
529
- raise "Could not execute command to start test server" unless system("#{cmd} 2>&1")
604
+ log "Starting test server using:"
605
+ log cmd
606
+ raise "Could not execute command to start test server" unless system("#{cmd} 2>&1")
530
607
 
531
- retriable :tries => 10, :interval => 1 do
532
- raise "App did not start" unless app_running?
533
- end
608
+ retriable :tries => 10, :interval => 1 do
609
+ raise "App did not start" unless app_running?
610
+ end
534
611
 
535
- begin
536
- retriable :tries => 10, :interval => 3 do
612
+ begin
613
+ retriable :tries => 10, :interval => 3 do
537
614
  log "Checking if instrumentation backend is ready"
538
615
 
539
616
  log "Is app running? #{app_running?}"
@@ -544,450 +621,567 @@ module Operations
544
621
  else
545
622
  log "Instrumentation backend is ready!"
546
623
  end
547
- end
548
- rescue Exception => e
624
+ end
625
+ rescue Exception => e
549
626
 
550
- msg = "Unable to make connection to Calabash Test Server at http://127.0.0.1:#{@server_port}/\n"
551
- msg << "Please check the logcat output for more info about what happened\n"
552
- raise msg
553
- end
627
+ msg = "Unable to make connection to Calabash Test Server at http://127.0.0.1:#{@server_port}/\n"
628
+ msg << "Please check the logcat output for more info about what happened\n"
629
+ raise msg
630
+ end
554
631
 
555
- log "Checking client-server version match..."
556
- response = perform_action('version')
557
- unless response['success']
558
- msg = ["Unable to obtain Test Server version. "]
559
- msg << "Please run 'reinstall_test_server' to make sure you have the correct version"
560
- msg_s = msg.join("\n")
561
- log(msg_s)
562
- raise msg_s
563
- end
564
- unless response['message'] == Calabash::Android::VERSION
632
+ log "Checking client-server version match..."
565
633
 
566
- msg = ["Calabash Client and Test-server version mismatch."]
567
- msg << "Client version #{Calabash::Android::VERSION}"
568
- msg << "Test-server version #{response['message']}"
569
- msg << "Expected Test-server version #{Calabash::Android::VERSION}"
570
- msg << "\n\nSolution:\n\n"
571
- msg << "Run 'reinstall_test_server' to make sure you have the correct version"
572
- msg_s = msg.join("\n")
573
- log(msg_s)
574
- raise msg_s
575
- end
576
- log("Client and server versions match. Proceeding...")
634
+ begin
635
+ server_version = server_version()
636
+ rescue
637
+ msg = ["Unable to obtain Test Server version. "]
638
+ msg << "Please run 'reinstall_test_server' to make sure you have the correct version"
639
+ msg_s = msg.join("\n")
640
+ log(msg_s)
641
+ raise msg_s
642
+ end
577
643
 
644
+ client_version = client_version()
645
+
646
+ unless server_version == client_version
647
+ msg = ["Calabash Client and Test-server version mismatch."]
648
+ msg << "Client version #{client_version}"
649
+ msg << "Test-server version #{server_version}"
650
+ msg << "Expected Test-server version #{client_version}"
651
+ msg << "\n\nSolution:\n\n"
652
+ msg << "Run 'reinstall_test_server' to make sure you have the correct version"
653
+ msg_s = msg.join("\n")
654
+ log(msg_s)
655
+ raise msg_s
656
+ end
578
657
 
579
- end
658
+ log("Client and server versions match (client: #{client_version}, server: #{server_version}). Proceeding...")
659
+ end
580
660
 
581
- def shutdown_test_server
582
- begin
583
- http("/kill")
584
- Timeout::timeout(3) do
585
- sleep 0.3 while app_running?
661
+ def shutdown_test_server
662
+ begin
663
+ http("/kill")
664
+ Timeout::timeout(3) do
665
+ sleep 0.3 while app_running?
666
+ end
667
+ rescue HTTPClient::KeepAliveDisconnected
668
+ log ("Server not responding. Moving on.")
669
+ rescue Timeout::Error
670
+ log ("Could not kill app. Waited to 3 seconds.")
671
+ rescue EOFError
672
+ log ("Could not kill app. App is most likely not running anymore.")
586
673
  end
587
- rescue HTTPClient::KeepAliveDisconnected
588
- log ("Server not responding. Moving on.")
589
- rescue Timeout::Error
590
- log ("Could not kill app. Waited to 3 seconds.")
591
- rescue EOFError
592
- log ("Could not kill app. App is most likely not running anymore.")
593
674
  end
594
- end
595
675
 
596
- ##location
597
- def set_gps_coordinates_from_location(location)
598
- require 'geocoder'
599
- results = Geocoder.search(location)
600
- raise Exception, "Got no results for #{location}" if results.empty?
676
+ ##location
677
+ def set_gps_coordinates_from_location(location)
678
+ require 'geocoder'
679
+ results = Geocoder.search(location)
680
+ raise Exception, "Got no results for #{location}" if results.empty?
601
681
 
602
- best_result = results.first
603
- set_gps_coordinates(best_result.latitude, best_result.longitude)
604
- end
682
+ best_result = results.first
683
+ set_gps_coordinates(best_result.latitude, best_result.longitude)
684
+ end
605
685
 
606
- def set_gps_coordinates(latitude, longitude)
607
- perform_action('set_gps_coordinates', latitude, longitude)
608
- end
686
+ def set_gps_coordinates(latitude, longitude)
687
+ perform_action('set_gps_coordinates', latitude, longitude)
688
+ end
609
689
 
610
- def get_preferences(name)
690
+ def get_preferences(name)
611
691
 
612
- log "Get preferences: #{name}, app running? #{app_running?}"
613
- preferences = {}
692
+ log "Get preferences: #{name}, app running? #{app_running?}"
693
+ preferences = {}
614
694
 
615
- if app_running?
616
- json = perform_action('get_preferences', name);
617
- else
695
+ if app_running?
696
+ json = perform_action('get_preferences', name);
697
+ else
618
698
 
619
- logcat_id = get_logcat_id()
620
- cmd = "#{adb_command} shell am instrument -e logcat #{logcat_id} -e name \"#{name}\" #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.GetPreferences"
699
+ logcat_id = get_logcat_id()
700
+ cmd = "#{adb_command} shell am instrument -e logcat #{logcat_id} -e name \"#{name}\" #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.GetPreferences"
621
701
 
622
- raise "Could not get preferences" unless system(cmd)
702
+ raise "Could not get preferences" unless system(cmd)
623
703
 
624
- logcat_cmd = get_logcat_cmd(logcat_id)
625
- logcat_output = `#{logcat_cmd}`
704
+ logcat_cmd = get_logcat_cmd(logcat_id)
705
+ logcat_output = `#{logcat_cmd}`
626
706
 
627
- json = get_json_from_logcat(logcat_output)
707
+ json = get_json_from_logcat(logcat_output)
628
708
 
629
- raise "Could not get preferences" unless json != nil and json["success"]
630
- end
709
+ raise "Could not get preferences" unless json != nil and json["success"]
710
+ end
631
711
 
632
- # at this point we have valid json, coming from an action
633
- # or instrumentation, but we don't care, just parse
634
- if json["bonusInformation"].length > 0
712
+ # at this point we have valid json, coming from an action
713
+ # or instrumentation, but we don't care, just parse
714
+ if json["bonusInformation"].length > 0
635
715
  json["bonusInformation"].each do |item|
636
- json_item = JSON.parse(item)
637
- preferences[json_item["key"]] = json_item["value"]
716
+ json_item = JSON.parse(item)
717
+ preferences[json_item["key"]] = json_item["value"]
718
+ end
638
719
  end
720
+
721
+ preferences
639
722
  end
640
723
 
641
- preferences
642
- end
724
+ def set_preferences(name, hash)
643
725
 
644
- def set_preferences(name, hash)
726
+ log "Set preferences: #{name}, #{hash}, app running? #{app_running?}"
645
727
 
646
- log "Set preferences: #{name}, #{hash}, app running? #{app_running?}"
647
-
648
- if app_running?
649
- perform_action('set_preferences', name, hash);
650
- else
728
+ if app_running?
729
+ perform_action('set_preferences', name, hash);
730
+ else
651
731
 
652
- params = hash.map {|k,v| "-e \"#{k}\" \"#{v}\""}.join(" ")
732
+ params = hash.map {|k,v| "-e \"#{k}\" \"#{v}\""}.join(" ")
653
733
 
654
- logcat_id = get_logcat_id()
655
- cmd = "#{adb_command} shell am instrument -e logcat #{logcat_id} -e name \"#{name}\" #{params} #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.SetPreferences"
734
+ logcat_id = get_logcat_id()
735
+ cmd = "#{adb_command} shell am instrument -e logcat #{logcat_id} -e name \"#{name}\" #{params} #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.SetPreferences"
656
736
 
657
- raise "Could not set preferences" unless system(cmd)
737
+ raise "Could not set preferences" unless system(cmd)
658
738
 
659
- logcat_cmd = get_logcat_cmd(logcat_id)
660
- logcat_output = `#{logcat_cmd}`
739
+ logcat_cmd = get_logcat_cmd(logcat_id)
740
+ logcat_output = `#{logcat_cmd}`
661
741
 
662
- json = get_json_from_logcat(logcat_output)
742
+ json = get_json_from_logcat(logcat_output)
663
743
 
664
- raise "Could not set preferences" unless json != nil and json["success"]
744
+ raise "Could not set preferences" unless json != nil and json["success"]
745
+ end
665
746
  end
666
- end
667
747
 
668
- def clear_preferences(name)
748
+ def clear_preferences(name)
669
749
 
670
- log "Clear preferences: #{name}, app running? #{app_running?}"
750
+ log "Clear preferences: #{name}, app running? #{app_running?}"
671
751
 
672
- if app_running?
673
- perform_action('clear_preferences', name);
674
- else
752
+ if app_running?
753
+ perform_action('clear_preferences', name);
754
+ else
675
755
 
676
- logcat_id = get_logcat_id()
677
- cmd = "#{adb_command} shell am instrument -e logcat #{logcat_id} -e name \"#{name}\" #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.ClearPreferences"
678
- raise "Could not clear preferences" unless system(cmd)
756
+ logcat_id = get_logcat_id()
757
+ cmd = "#{adb_command} shell am instrument -e logcat #{logcat_id} -e name \"#{name}\" #{package_name(@test_server_path)}/sh.calaba.instrumentationbackend.ClearPreferences"
758
+ raise "Could not clear preferences" unless system(cmd)
679
759
 
680
- logcat_cmd = get_logcat_cmd(logcat_id)
681
- logcat_output = `#{logcat_cmd}`
760
+ logcat_cmd = get_logcat_cmd(logcat_id)
761
+ logcat_output = `#{logcat_cmd}`
682
762
 
683
- json = get_json_from_logcat(logcat_output)
763
+ json = get_json_from_logcat(logcat_output)
684
764
 
685
- raise "Could not clear preferences" unless json != nil and json["success"]
765
+ raise "Could not clear preferences" unless json != nil and json["success"]
766
+ end
686
767
  end
687
- end
688
768
 
689
- def get_json_from_logcat(logcat_output)
769
+ def get_json_from_logcat(logcat_output)
690
770
 
691
- logcat_output.split(/\r?\n/).each do |line|
692
- begin
693
- json = JSON.parse(line)
694
- return json
695
- rescue
696
- # nothing to do here, just discarding logcat rubbish
771
+ logcat_output.split(/\r?\n/).each do |line|
772
+ begin
773
+ json = JSON.parse(line)
774
+ return json
775
+ rescue
776
+ # nothing to do here, just discarding logcat rubbish
777
+ end
697
778
  end
779
+
780
+ return nil
781
+ end
782
+
783
+ def get_logcat_id()
784
+ # we need a unique logcat tag so we can later
785
+ # query the logcat output and filter out everything
786
+ # but what we are interested in
787
+
788
+ random = (0..10000).to_a.sample
789
+ "#{Time.now.strftime("%s")}_#{random}"
790
+ end
791
+
792
+ def get_logcat_cmd(tag)
793
+ # returns raw logcat output for our tag
794
+ # filtering out everthing else
795
+
796
+ "#{adb_command} logcat -d -v raw #{tag}:* *:S"
698
797
  end
798
+ end
699
799
 
700
- return nil
800
+ def label(uiquery)
801
+ ni
701
802
  end
702
803
 
703
- def get_logcat_id()
704
- # we need a unique logcat tag so we can later
705
- # query the logcat output and filter out everything
706
- # but what we are interested in
804
+ def screenshot_and_raise(msg, options = nil)
805
+ if options
806
+ screenshot_embed options
807
+ else
808
+ screenshot_embed
809
+ end
810
+ raise(msg)
811
+ end
707
812
 
708
- random = (0..10000).to_a.sample
709
- "#{Time.now.strftime("%s")}_#{random}"
813
+ def hide_soft_keyboard
814
+ perform_action('hide_soft_keyboard')
710
815
  end
711
816
 
712
- def get_logcat_cmd(tag)
713
- # returns raw logcat output for our tag
714
- # filtering out everthing else
817
+ def execute_uiquery(uiquery)
818
+ if uiquery.instance_of? String
819
+ elements = query(uiquery)
820
+
821
+ return elements.first unless elements.empty?
822
+ else
823
+ elements = uiquery
824
+
825
+ return elements.first if elements.instance_of?(Array)
826
+ return elements if elements.instance_of?(Hash)
827
+ end
715
828
 
716
- "#{adb_command} logcat -d -v raw #{tag}:* *:S"
829
+ nil
717
830
  end
718
- end
719
831
 
720
- def label(uiquery)
721
- ni
722
- end
832
+ def step_deprecated
833
+ puts 'Warning: This predefined step is deprecated.'
834
+ end
723
835
 
724
- def screenshot_and_raise(msg, options = nil)
725
- if options
726
- screenshot_embed options
727
- else
728
- screenshot_embed
836
+ def http(path, data = {}, options = {})
837
+ default_device.http(path, data, options)
729
838
  end
730
- raise(msg)
731
- end
732
839
 
733
- def hide_soft_keyboard
734
- perform_action('hide_soft_keyboard')
735
- end
840
+ def html(q)
841
+ query(q).map {|e| e['html']}
842
+ end
736
843
 
737
- def execute_uiquery(uiquery)
738
- if uiquery.instance_of? String
739
- elements = query(uiquery)
844
+ def set_text(uiquery, txt)
845
+ puts "set_text is deprecated. Use enter_text instead"
846
+ enter_text(uiquery, txt)
847
+ end
740
848
 
741
- return elements.first unless elements.empty?
742
- else
743
- elements = uiquery
849
+ def press_user_action_button(action_name=nil)
850
+ if action_name.nil?
851
+ perform_action("press_user_action_button")
852
+ else
853
+ perform_action("press_user_action_button", action_name)
854
+ end
855
+ end
744
856
 
745
- return elements.first if elements.instance_of?(Array)
746
- return elements if elements.instance_of?(Hash)
857
+ def press_button(key)
858
+ perform_action('press_key', key)
747
859
  end
748
860
 
749
- nil
750
- end
861
+ def press_back_button
862
+ press_button('KEYCODE_BACK')
863
+ end
751
864
 
752
- def step_deprecated
753
- puts 'Warning: This predefined step is deprecated.'
754
- end
865
+ def press_menu_button
866
+ press_button('KEYCODE_MENU')
867
+ end
755
868
 
756
- def http(path, data = {}, options = {})
757
- default_device.http(path, data, options)
758
- end
869
+ def press_down_button
870
+ press_button('KEYCODE_DPAD_DOWN')
871
+ end
759
872
 
760
- def html(q)
761
- query(q).map {|e| e['html']}
762
- end
873
+ def press_up_button
874
+ press_button('KEYCODE_DPAD_UP')
875
+ end
763
876
 
764
- def set_text(uiquery, txt)
765
- puts "set_text is deprecated. Use enter_text instead"
766
- enter_text(uiquery, txt)
767
- end
877
+ def press_left_button
878
+ press_button('KEYCODE_DPAD_LEFT')
879
+ end
768
880
 
769
- def press_back_button
770
- perform_action('go_back')
771
- end
881
+ def press_right_button
882
+ press_button('KEYCODE_DPAD_RIGHT')
883
+ end
772
884
 
773
- def press_menu_button
774
- perform_action('press_menu')
775
- end
885
+ def press_enter_button
886
+ press_button('KEYCODE_ENTER')
887
+ end
776
888
 
777
- def select_options_menu_item(identifier, options={})
778
- press_menu_button
779
- tap_when_element_exists("DropDownListView * marked:'#{identifier}'", options)
780
- end
889
+ def select_options_menu_item(identifier, options={})
890
+ press_menu_button
891
+ tap_when_element_exists("DropDownListView * marked:'#{identifier}'", options)
892
+ end
781
893
 
782
- def select_context_menu_item(view_uiquery, menu_item_query_string)
783
- long_press(view_uiquery)
894
+ def select_context_menu_item(view_uiquery, menu_item_query_string)
895
+ long_press(view_uiquery)
784
896
 
785
- container_class = 'com.android.internal.view.menu.ListMenuItemView'
786
- wait_for_element_exists(container_class)
897
+ container_class = 'com.android.internal.view.menu.ListMenuItemView'
898
+ wait_for_element_exists(container_class)
787
899
 
788
- combined_query_string = "#{container_class} descendant #{menu_item_query_string}"
789
- touch(combined_query_string)
790
- end
900
+ combined_query_string = "#{container_class} descendant #{menu_item_query_string}"
901
+ touch(combined_query_string)
902
+ end
791
903
 
792
- def swipe(dir,options={})
793
- ni
794
- end
904
+ def select_item_from_spinner(item_query_string, options={})
905
+ spinner_query_string = options[:spinner] || "android.widget.AbsSpinner"
906
+ direction = options[:direction] || :down
907
+ count = query(spinner_query_string, :getCount).first
908
+ scroll_view_query_string = options[:scroll_view] || "android.widget.AbsListView index:0"
795
909
 
796
- def cell_swipe(options={})
797
- ni
798
- end
910
+ unless direction == :up || direction == :down
911
+ raise "Invalid direction '#{direction}'. Only upwards and downwards scrolling is supported"
912
+ end
799
913
 
800
- def done
801
- ni
802
- end
914
+ touch(spinner_query_string)
915
+
916
+ change_direction = false
917
+
918
+ wait_for({retry_frequency: 0}.merge(options)) do
919
+ if query(item_query_string).empty?
920
+ scroll(scroll_view_query_string, direction)
921
+
922
+ if change_direction
923
+ direction = direction == :up ? :down : :up
924
+ change_direction = false
925
+ else
926
+ # Because getLastVisiblePosition returns the last element even though it is not visible,
927
+ # we have to scroll one more time to make sure we do not change direction before the last
928
+ # element is fully visible
929
+ if direction == :down
930
+ change_direction = true if query(scroll_view_query_string, :getLastVisiblePosition).first+1 == count
931
+ elsif direction == :up
932
+ change_direction = true if query(scroll_view_query_string, :getFirstVisiblePosition).first == 0
933
+ end
934
+ end
803
935
 
804
- def scroll_up
805
- scroll("android.widget.ScrollView", :up)
806
- end
936
+ false
937
+ else
938
+ true
939
+ end
940
+ end
807
941
 
808
- def scroll_down
809
- scroll("android.widget.ScrollView", :down)
810
- end
942
+ touch(item_query_string)
943
+ end
811
944
 
812
- def scroll(query_string, direction)
813
- if direction != :up && direction != :down
814
- raise 'Only upwards and downwards scrolling is supported for now'
945
+ def swipe(query_string, options={})
946
+ raise 'Swipe not implemented. Use flick or pan instead.'
815
947
  end
816
948
 
817
- scroll_x = 0
818
- scroll_y = 0
949
+ def cell_swipe(options={})
950
+ ni
951
+ end
819
952
 
820
- action = lambda do
821
- element = query(query_string).first
822
- raise "No elements found. Query: #{query_string}" if element.nil?
953
+ def done
954
+ ni
955
+ end
823
956
 
824
- width = element['rect']['width']
825
- height = element['rect']['height']
957
+ def find_scrollable_view(options={})
958
+ timeout = options[:timeout] || 30
826
959
 
827
- if direction == :up
828
- scroll_y = -height/2
829
- else
830
- scroll_y = height/2
960
+ begin
961
+ Timeout.timeout(timeout, WaitError) do
962
+ scroll_view_query_string = "android.widget.ScrollView index:0"
963
+ list_view_query_string = "android.widget.AbsListView index:0"
964
+ web_view_query_string = "android.webkit.WebView index:0"
965
+
966
+ loop do
967
+ if element_exists(scroll_view_query_string)
968
+ return scroll_view_query_string
969
+ elsif element_exists(list_view_query_string)
970
+ return list_view_query_string
971
+ elsif element_exists(web_view_query_string)
972
+ return web_view_query_string
973
+ end
974
+ end
975
+ end
976
+ rescue WaitError
977
+ raise WaitError.new('Could not find any scrollable views')
831
978
  end
979
+ end
832
980
 
833
- query(query_string, {scrollBy: [scroll_x.to_i, scroll_y.to_i]})
981
+ def scroll_up(options={})
982
+ scroll(find_scrollable_view(options), :up)
834
983
  end
835
984
 
836
- when_element_exists(query_string, action: action)
837
- end
985
+ def scroll_down(options={})
986
+ scroll(find_scrollable_view(options), :down)
987
+ end
838
988
 
839
- def scroll_to(query_string, options={})
840
- options[:action] ||= lambda {}
989
+ def scroll(query_string, direction)
990
+ if direction != :up && direction != :down
991
+ raise 'Only upwards and downwards scrolling is supported for now'
992
+ end
841
993
 
842
- all_query_string = query_string
994
+ action = lambda do
995
+ elements = query(query_string)
996
+ raise "No elements found. Query: #{query_string}" if elements.empty?
843
997
 
844
- unless all_query_string.chomp.downcase.start_with?('all')
845
- all_query_string = "all #{all_query_string}"
846
- end
998
+ if elements.length > 1
999
+ query_string = "#{query_string} index:0"
1000
+ end
847
1001
 
848
- wait_for_element_exists(all_query_string)
1002
+ element = elements.first
849
1003
 
850
- visibility_query_string = all_query_string[4..-1]
1004
+ response = query(query_string, :getFirstVisiblePosition).first
851
1005
 
852
- unless query(visibility_query_string).empty?
853
- when_element_exists(visibility_query_string, options)
854
- return
855
- end
1006
+ if response.is_a?(Hash) && response.has_key?("error") # View is not of type android.widget.AbsListView
1007
+ scroll_x = 0
1008
+ scroll_y = 0
1009
+ width = element['rect']['width']
1010
+ height = element['rect']['height']
1011
+
1012
+ if direction == :up
1013
+ scroll_y = -height/2
1014
+ else
1015
+ scroll_y = height/2
1016
+ end
1017
+
1018
+ query(query_string, {scrollBy: [scroll_x.to_i, scroll_y.to_i]})
1019
+ else # View is of type android.widget.AbsListView
1020
+ first_position = response.to_i
1021
+ last_position = query(query_string, :getLastVisiblePosition).first.to_i
1022
+
1023
+ selection_index = if direction == :up
1024
+ [first_position + [first_position - last_position + 1, -1].min, 0].max
1025
+ elsif direction == :down
1026
+ first_position + [last_position - first_position, 1].max
1027
+ end
856
1028
 
857
- element = query(all_query_string).first
858
- raise "No elements found. Query: #{all_query_string}" if element.nil?
859
- element_center_y = element['rect']['center_y']
1029
+ query(query_string, setSelection: selection_index)
1030
+ end
1031
+ end
860
1032
 
861
- if element.has_key?('html')
862
- scroll_view_query_string = element['webView']
863
- else
864
- scroll_view_query_string = "#{all_query_string} parent android.widget.ScrollView index:0"
1033
+ when_element_exists(query_string, action: action)
865
1034
  end
866
1035
 
867
- scroll_element = query(scroll_view_query_string).first
1036
+ def scroll_to(query_string, options={})
1037
+ options[:action] ||= lambda {}
868
1038
 
869
- raise "Could not find parent scroll view. Query: #{scroll_view_query_string}" if element.nil?
1039
+ all_query_string = query_string
870
1040
 
871
- scroll_element_y = scroll_element['rect']['y']
872
- scroll_element_height = scroll_element['rect']['height']
1041
+ unless all_query_string.chomp.downcase.start_with?('all')
1042
+ all_query_string = "all #{all_query_string}"
1043
+ end
873
1044
 
874
- if element_center_y > scroll_element_y + scroll_element_height
875
- scroll_by_y = element_center_y - (scroll_element_y + scroll_element_height) + 2
876
- else
877
- scroll_by_y = element_center_y - scroll_element_y - 2
878
- end
1045
+ wait_for_element_exists(all_query_string)
879
1046
 
880
- result = query(scroll_view_query_string, {scrollBy: [0, scroll_by_y.to_i]}).first
881
- raise 'Could not scroll parent view' if result != '<VOID>'
1047
+ element = query(all_query_string).first
1048
+ raise "No elements found. Query: #{all_query_string}" if element.nil?
1049
+ element_y = element['rect']['y']
1050
+ element_height = element['rect']['height']
1051
+ element_bottom = element_y + element_height
882
1052
 
883
- visibility_query_string = all_query_string[4..-1]
884
- when_element_exists(visibility_query_string, options)
885
- end
1053
+ scroll_view_query_string = options[:container] || if element.has_key?('html')
1054
+ "android.webkit.WebView id:'#{element['webView']}'"
1055
+ else
1056
+ "#{all_query_string} parent android.widget.ScrollView index:0"
1057
+ end
886
1058
 
887
- def scroll_to_row(uiquery,number)
888
- query(uiquery, {:smoothScrollToPosition => number})
889
- puts "TODO:detect end of scroll - use sleep for now"
890
- end
1059
+ scroll_element = query(scroll_view_query_string).first
891
1060
 
892
- def pinch(in_out,options={})
893
- ni
894
- end
1061
+ raise "Could not find parent scroll view. Query: '#{escape_quotes(scroll_view_query_string)}'" if scroll_element.nil?
895
1062
 
896
- def rotate(dir)
897
- ni
898
- end
1063
+ scroll_element_y = scroll_element['rect']['y']
1064
+ scroll_element_height = scroll_element['rect']['height']
899
1065
 
900
- def app_to_background(secs)
901
- ni
902
- end
1066
+ if element_bottom > scroll_element_y + scroll_element_height
1067
+ scroll_by_y = element_bottom - (scroll_element_y + scroll_element_height)
1068
+ elsif element_y < scroll_element_y
1069
+ scroll_by_y = element_y - scroll_element_y
1070
+ else
1071
+ scroll_by_y = 0
1072
+ end
903
1073
 
904
- def element_does_not_exist(uiquery)
905
- query(uiquery).empty?
906
- end
1074
+ if scroll_by_y != 0
1075
+ result = query(scroll_view_query_string, {scrollBy: [0, scroll_by_y]}).first
1076
+ raise 'Could not scroll parent view' if result != '<VOID>'
1077
+ end
907
1078
 
908
- def element_exists(uiquery)
909
- not element_does_not_exist(uiquery)
910
- end
1079
+ visibility_query_string = all_query_string[4..-1]
1080
+ when_element_exists(visibility_query_string, options)
1081
+ end
911
1082
 
912
- def view_with_mark_exists(expected_mark)
913
- element_exists( "android.view.View marked:'#{expected_mark}'" )
914
- end
1083
+ def scroll_to_row(uiquery,number)
1084
+ query(uiquery, {:smoothScrollToPosition => number})
1085
+ puts "TODO:detect end of scroll - use sleep for now"
1086
+ end
915
1087
 
916
- def check_element_exists( query )
917
- if not element_exists( query )
918
- screenshot_and_raise "No element found for query: #{query}"
1088
+ def rotate(dir)
1089
+ ni
919
1090
  end
920
- end
921
1091
 
922
- def check_element_does_not_exist( query )
923
- if element_exists( query )
924
- screenshot_and_raise "Expected no elements to match query: #{query}"
1092
+ def app_to_background(secs)
1093
+ ni
925
1094
  end
926
- end
927
1095
 
928
- def check_view_with_mark_exists(expected_mark)
929
- check_element_exists( "view marked:'#{expected_mark}'" )
930
- end
1096
+ def element_does_not_exist(uiquery)
1097
+ query(uiquery).empty?
1098
+ end
931
1099
 
932
- # a better name would be element_exists_and_is_not_hidden
933
- def element_is_not_hidden(uiquery)
934
- ni
935
- end
1100
+ def element_exists(uiquery)
1101
+ not element_does_not_exist(uiquery)
1102
+ end
936
1103
 
1104
+ def view_with_mark_exists(expected_mark)
1105
+ element_exists( "android.view.View marked:'#{expected_mark}'" )
1106
+ end
937
1107
 
938
- def load_playback_data(recording,options={})
939
- ni
940
- end
1108
+ def check_element_exists( query )
1109
+ if not element_exists( query )
1110
+ screenshot_and_raise "No element found for query: #{query}"
1111
+ end
1112
+ end
941
1113
 
942
- def playback(recording, options={})
943
- ni
944
- end
1114
+ def check_element_does_not_exist( query )
1115
+ if element_exists( query )
1116
+ screenshot_and_raise "Expected no elements to match query: #{query}"
1117
+ end
1118
+ end
945
1119
 
946
- def interpolate(recording, options={})
947
- ni
948
- end
1120
+ def check_view_with_mark_exists(expected_mark)
1121
+ check_element_exists( "view marked:'#{expected_mark}'" )
1122
+ end
949
1123
 
950
- def record_begin
951
- ni
952
- end
1124
+ # a better name would be element_exists_and_is_not_hidden
1125
+ def element_is_not_hidden(uiquery)
1126
+ ni
1127
+ end
953
1128
 
954
- def record_end(file_name)
955
- ni
956
- end
957
1129
 
958
- def backdoor(sel, arg)
959
- result = perform_action("backdoor", sel, arg)
960
- if !result["success"]
961
- screenshot_and_raise(result["message"])
1130
+ def load_playback_data(recording,options={})
1131
+ ni
962
1132
  end
963
1133
 
964
- # for android results are returned in bonusInformation
965
- result["bonusInformation"].first
966
- end
1134
+ def playback(recording, options={})
1135
+ ni
1136
+ end
967
1137
 
968
- def map(query, method_name, *method_args)
969
- operation_map = {
970
- :method_name => method_name,
971
- :arguments => method_args
972
- }
973
- res = http("/map",
974
- {:query => query, :operation => operation_map})
975
- res = JSON.parse(res)
976
- if res['outcome'] != 'SUCCESS'
977
- screenshot_and_raise "map #{query}, #{method_name} failed because: #{res['reason']}\n#{res['details']}"
1138
+ def interpolate(recording, options={})
1139
+ ni
978
1140
  end
979
1141
 
980
- res['results']
981
- end
1142
+ def record_begin
1143
+ ni
1144
+ end
982
1145
 
983
- def url_for( method )
984
- default_device.url_for(method)
985
- end
1146
+ def record_end(file_name)
1147
+ ni
1148
+ end
986
1149
 
987
- def make_http_request(options)
988
- default_device.make_http_request(options)
1150
+ def backdoor(method_name, arguments = [], options={})
1151
+ arguments = [arguments] unless arguments.is_a?(Array)
1152
+
1153
+ result = JSON.parse(http('/backdoor', {method_name: method_name, arguments: arguments}))
1154
+
1155
+ if result['outcome'] != 'SUCCESS'
1156
+ raise result.to_s
1157
+ end
1158
+
1159
+ result['result']
1160
+ end
1161
+
1162
+ def map(query, method_name, *method_args)
1163
+ operation_map = {
1164
+ :method_name => method_name,
1165
+ :arguments => method_args
1166
+ }
1167
+ res = http("/map",
1168
+ {:query => query, :operation => operation_map})
1169
+ res = JSON.parse(res)
1170
+ if res['outcome'] != 'SUCCESS'
1171
+ screenshot_and_raise "map #{query}, #{method_name} failed because: #{res['reason']}\n#{res['details']}"
1172
+ end
1173
+
1174
+ res['results']
1175
+ end
1176
+
1177
+ def url_for( method )
1178
+ default_device.url_for(method)
1179
+ end
1180
+
1181
+ def make_http_request(options)
1182
+ default_device.make_http_request(options)
1183
+ end
989
1184
  end
990
- end
991
1185
 
992
1186
 
993
- end end
1187
+ end end