calabash-android 0.5.1 → 0.5.2.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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