testautoi 0.9.127

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 (121) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGES.txt +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +8 -0
  5. data/Rakefile +2 -0
  6. data/bin/CalabashSetup +0 -0
  7. data/bin/cal.xcconfig +3 -0
  8. data/bin/calabash-ios +79 -0
  9. data/bin/calabash-ios-build.rb +110 -0
  10. data/bin/calabash-ios-generate.rb +26 -0
  11. data/bin/calabash-ios-helpers.rb +130 -0
  12. data/bin/calabash-ios-setup.rb +334 -0
  13. data/bin/calabash-ios-sim.rb +149 -0
  14. data/calabash-cucumber.gemspec +29 -0
  15. data/doc/calabash-ios-help.txt +73 -0
  16. data/epl-v10.html +261 -0
  17. data/features-skeleton/.irbrc +23 -0
  18. data/features-skeleton/irb_ios4.sh +2 -0
  19. data/features-skeleton/irb_ios5.sh +2 -0
  20. data/features-skeleton/my_first.feature +12 -0
  21. data/features-skeleton/step_definitions/calabash_steps.rb +1 -0
  22. data/features-skeleton/step_definitions/my_first_steps.rb +4 -0
  23. data/features-skeleton/support/env.rb +1 -0
  24. data/features-skeleton/support/hooks.rb +0 -0
  25. data/features-skeleton/support/launch.rb +77 -0
  26. data/features/step_definitions/calabash_steps.rb +439 -0
  27. data/lib/calabash-cucumber.rb +7 -0
  28. data/lib/calabash-cucumber/calabash_steps.rb +1 -0
  29. data/lib/calabash-cucumber/core.rb +553 -0
  30. data/lib/calabash-cucumber/cucumber.rb +8 -0
  31. data/lib/calabash-cucumber/ibase.rb +45 -0
  32. data/lib/calabash-cucumber/keyboard_helpers.rb +124 -0
  33. data/lib/calabash-cucumber/launch/simulator_helper.rb +297 -0
  34. data/lib/calabash-cucumber/location.rb +26 -0
  35. data/lib/calabash-cucumber/operations.rb +162 -0
  36. data/lib/calabash-cucumber/resources/cell_swipe_ios4_ipad.base64 +51 -0
  37. data/lib/calabash-cucumber/resources/cell_swipe_ios4_iphone.base64 +51 -0
  38. data/lib/calabash-cucumber/resources/cell_swipe_ios5_ipad.base64 +74 -0
  39. data/lib/calabash-cucumber/resources/cell_swipe_ios5_iphone.base64 +74 -0
  40. data/lib/calabash-cucumber/resources/pinch_in_ios4_ipad.base64 +104 -0
  41. data/lib/calabash-cucumber/resources/pinch_in_ios4_iphone.base64 +104 -0
  42. data/lib/calabash-cucumber/resources/pinch_in_ios5_ipad.base64 +144 -0
  43. data/lib/calabash-cucumber/resources/pinch_in_ios5_iphone.base64 +144 -0
  44. data/lib/calabash-cucumber/resources/pinch_in_ios6_ipad.base64 +70 -0
  45. data/lib/calabash-cucumber/resources/pinch_in_ios6_iphone.base64 +70 -0
  46. data/lib/calabash-cucumber/resources/pinch_out_ios5_ipad.base64 +207 -0
  47. data/lib/calabash-cucumber/resources/pinch_out_ios5_iphone.base64 +207 -0
  48. data/lib/calabash-cucumber/resources/pinch_out_ios6_ipad.base64 +96 -0
  49. data/lib/calabash-cucumber/resources/pinch_out_ios6_iphone.base64 +96 -0
  50. data/lib/calabash-cucumber/resources/rotate_left_home_down_ios4_ipad.base64 +2 -0
  51. data/lib/calabash-cucumber/resources/rotate_left_home_down_ios4_iphone.base64 +2 -0
  52. data/lib/calabash-cucumber/resources/rotate_left_home_down_ios5_ipad.base64 +2 -0
  53. data/lib/calabash-cucumber/resources/rotate_left_home_down_ios5_iphone.base64 +2 -0
  54. data/lib/calabash-cucumber/resources/rotate_left_home_left_ios4_ipad.base64 +2 -0
  55. data/lib/calabash-cucumber/resources/rotate_left_home_left_ios4_iphone.base64 +2 -0
  56. data/lib/calabash-cucumber/resources/rotate_left_home_left_ios5_ipad.base64 +2 -0
  57. data/lib/calabash-cucumber/resources/rotate_left_home_left_ios5_iphone.base64 +2 -0
  58. data/lib/calabash-cucumber/resources/rotate_left_home_right_ios4_ipad.base64 +2 -0
  59. data/lib/calabash-cucumber/resources/rotate_left_home_right_ios4_iphone.base64 +2 -0
  60. data/lib/calabash-cucumber/resources/rotate_left_home_right_ios5_ipad.base64 +2 -0
  61. data/lib/calabash-cucumber/resources/rotate_left_home_right_ios5_iphone.base64 +2 -0
  62. data/lib/calabash-cucumber/resources/rotate_left_home_up_ios4_ipad.base64 +2 -0
  63. data/lib/calabash-cucumber/resources/rotate_left_home_up_ios4_iphone.base64 +2 -0
  64. data/lib/calabash-cucumber/resources/rotate_left_home_up_ios5_ipad.base64 +2 -0
  65. data/lib/calabash-cucumber/resources/rotate_left_home_up_ios5_iphone.base64 +2 -0
  66. data/lib/calabash-cucumber/resources/rotate_right_home_down_ios4_ipad.base64 +2 -0
  67. data/lib/calabash-cucumber/resources/rotate_right_home_down_ios4_iphone.base64 +2 -0
  68. data/lib/calabash-cucumber/resources/rotate_right_home_down_ios5_ipad.base64 +2 -0
  69. data/lib/calabash-cucumber/resources/rotate_right_home_down_ios5_iphone.base64 +2 -0
  70. data/lib/calabash-cucumber/resources/rotate_right_home_left_ios4_ipad.base64 +2 -0
  71. data/lib/calabash-cucumber/resources/rotate_right_home_left_ios4_iphone.base64 +2 -0
  72. data/lib/calabash-cucumber/resources/rotate_right_home_left_ios5_ipad.base64 +2 -0
  73. data/lib/calabash-cucumber/resources/rotate_right_home_left_ios5_iphone.base64 +2 -0
  74. data/lib/calabash-cucumber/resources/rotate_right_home_right_ios4_ipad.base64 +2 -0
  75. data/lib/calabash-cucumber/resources/rotate_right_home_right_ios4_iphone.base64 +2 -0
  76. data/lib/calabash-cucumber/resources/rotate_right_home_right_ios5_ipad.base64 +2 -0
  77. data/lib/calabash-cucumber/resources/rotate_right_home_right_ios5_iphone.base64 +2 -0
  78. data/lib/calabash-cucumber/resources/rotate_right_home_up_ios4_ipad.base64 +2 -0
  79. data/lib/calabash-cucumber/resources/rotate_right_home_up_ios4_iphone.base64 +2 -0
  80. data/lib/calabash-cucumber/resources/rotate_right_home_up_ios5_ipad.base64 +2 -0
  81. data/lib/calabash-cucumber/resources/rotate_right_home_up_ios5_iphone.base64 +2 -0
  82. data/lib/calabash-cucumber/resources/swipe_down_ios5_ipad.base64 +18 -0
  83. data/lib/calabash-cucumber/resources/swipe_down_ios5_iphone.base64 +18 -0
  84. data/lib/calabash-cucumber/resources/swipe_left_hard_ios4_ipad.base64 +15 -0
  85. data/lib/calabash-cucumber/resources/swipe_left_hard_ios4_iphone.base64 +15 -0
  86. data/lib/calabash-cucumber/resources/swipe_left_ios4_ipad.base64 +18 -0
  87. data/lib/calabash-cucumber/resources/swipe_left_ios4_iphone.base64 +18 -0
  88. data/lib/calabash-cucumber/resources/swipe_left_ios5_ipad.base64 +17 -0
  89. data/lib/calabash-cucumber/resources/swipe_left_ios5_iphone.base64 +17 -0
  90. data/lib/calabash-cucumber/resources/swipe_right_hard_ios4_ipad.base64 +17 -0
  91. data/lib/calabash-cucumber/resources/swipe_right_hard_ios4_iphone.base64 +17 -0
  92. data/lib/calabash-cucumber/resources/swipe_right_ios4_ipad.base64 +13 -0
  93. data/lib/calabash-cucumber/resources/swipe_right_ios4_iphone.base64 +13 -0
  94. data/lib/calabash-cucumber/resources/swipe_right_ios5_ipad.base64 +17 -0
  95. data/lib/calabash-cucumber/resources/swipe_right_ios5_iphone.base64 +17 -0
  96. data/lib/calabash-cucumber/resources/swipe_up_ios5_ipad.base64 +34 -0
  97. data/lib/calabash-cucumber/resources/swipe_up_ios5_iphone.base64 +34 -0
  98. data/lib/calabash-cucumber/resources/touch_done_ios4_ipad.base64 +7 -0
  99. data/lib/calabash-cucumber/resources/touch_done_ios4_iphone.base64 +9 -0
  100. data/lib/calabash-cucumber/resources/touch_done_ios5_ipad.base64 +7 -0
  101. data/lib/calabash-cucumber/resources/touch_done_ios5_iphone.base64 +9 -0
  102. data/lib/calabash-cucumber/resources/touch_ios4_ipad.base64 +9 -0
  103. data/lib/calabash-cucumber/resources/touch_ios4_iphone.base64 +9 -0
  104. data/lib/calabash-cucumber/resources/touch_ios5_ipad.base64 +9 -0
  105. data/lib/calabash-cucumber/resources/touch_ios5_iphone.base64 +9 -0
  106. data/lib/calabash-cucumber/resources/wheel_down_ios4_ipad.base64 +159 -0
  107. data/lib/calabash-cucumber/resources/wheel_down_ios4_iphone.base64 +159 -0
  108. data/lib/calabash-cucumber/resources/wheel_down_ios5_ipad.base64 +156 -0
  109. data/lib/calabash-cucumber/resources/wheel_down_ios5_iphone.base64 +156 -0
  110. data/lib/calabash-cucumber/resources/wheel_up_ios4_ipad.base64 +166 -0
  111. data/lib/calabash-cucumber/resources/wheel_up_ios4_iphone.base64 +166 -0
  112. data/lib/calabash-cucumber/resources/wheel_up_ios5_ipad.base64 +156 -0
  113. data/lib/calabash-cucumber/resources/wheel_up_ios5_iphone.base64 +156 -0
  114. data/lib/calabash-cucumber/tests_helpers.rb +136 -0
  115. data/lib/calabash-cucumber/version.rb +6 -0
  116. data/lib/calabash-cucumber/wait_helpers.rb +149 -0
  117. data/scripts/data/.GlobalPreferences.plist +0 -0
  118. data/scripts/data/clients.plist +0 -0
  119. data/scripts/data/com.apple.Accessibility.plist +0 -0
  120. data/scripts/reset_simulator.scpt +0 -0
  121. metadata +319 -0
@@ -0,0 +1,7 @@
1
+ require 'calabash-cucumber/core'
2
+ require 'calabash-cucumber/tests_helpers'
3
+ require 'calabash-cucumber/keyboard_helpers'
4
+ require 'calabash-cucumber/wait_helpers'
5
+ require 'calabash-cucumber/operations'
6
+ require 'calabash-cucumber/version'
7
+ require 'calabash-cucumber/location'
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__),'..','..','features','step_definitions',"calabash_steps")
@@ -0,0 +1,553 @@
1
+ require 'httpclient'
2
+ require 'calabash-cucumber/launch/simulator_helper'
3
+
4
+ module Calabash
5
+ module Cucumber
6
+ module Core
7
+
8
+ DATA_PATH = File.expand_path(File.dirname(__FILE__))
9
+ CAL_HTTP_RETRY_COUNT=3
10
+ RETRYABLE_ERRORS = [Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::ETIMEDOUT]
11
+
12
+ def macro(txt)
13
+ if self.respond_to? :step
14
+ step(txt)
15
+ else
16
+ Then txt
17
+ end
18
+ end
19
+
20
+ def query(uiquery, *args)
21
+ map(uiquery, :query, *args)
22
+ end
23
+
24
+ def server_version
25
+ JSON.parse(http(:path => 'version'))
26
+ end
27
+
28
+ def client_version
29
+ Calabash::Cucumber::VERSION
30
+ end
31
+
32
+ def perform(*args)
33
+ if args.length == 1
34
+ #simple selector
35
+ hash = args.first
36
+ q = hash[:on]
37
+ hash = hash.dup
38
+ hash.delete(:on)
39
+ args = [hash]
40
+ elsif args.length == 2
41
+ q = args[1][:on]
42
+ if args[0].is_a? Hash
43
+ args = [args[0]]
44
+ else
45
+ args = args[0]
46
+ end
47
+ end
48
+ map(q, :query_all, *args)
49
+ end
50
+
51
+ def query_all(uiquery, *args)
52
+ map(uiquery, :query_all, *args)
53
+ end
54
+
55
+ def touch(uiquery, options={})
56
+ options[:query] = uiquery
57
+ views_touched = playback("touch", options)
58
+ unless uiquery.nil?
59
+ screenshot_and_raise "could not find view to touch: '#{uiquery}', args: #{options}" if views_touched.empty?
60
+ end
61
+ views_touched
62
+ end
63
+
64
+ def swipe(dir, options={})
65
+ dir = dir.to_sym
66
+ @current_rotation = @current_rotation || :down
67
+ if @current_rotation == :left
68
+ case dir
69
+ when :left then
70
+ dir = :down
71
+ when :right then
72
+ dir = :up
73
+ when :up then
74
+ dir = :left
75
+ when :down then
76
+ dir = :right
77
+ else
78
+ end
79
+ end
80
+ if @current_rotation == :right
81
+ case dir
82
+ when :left then
83
+ dir = :up
84
+ when :right then
85
+ dir = :down
86
+ when :up then
87
+ dir = :right
88
+ when :down then
89
+ dir = :left
90
+ else
91
+ end
92
+ end
93
+ if @current_rotation == :up
94
+ case dir
95
+ when :left then
96
+ dir = :right
97
+ when :right then
98
+ dir = :left
99
+ when :up then
100
+ dir = :down
101
+ when :down then
102
+ dir = :up
103
+ else
104
+ end
105
+ end
106
+ playback("swipe_#{dir}", options)
107
+ end
108
+
109
+ def cell_swipe(options={})
110
+ playback("cell_swipe", options)
111
+ end
112
+
113
+ def scroll(uiquery, direction)
114
+ views_touched=map(uiquery, :scroll, direction)
115
+ screenshot_and_raise "could not find view to scroll: '#{uiquery}', args: #{direction}" if views_touched.empty?
116
+ views_touched
117
+ end
118
+
119
+ def scroll_to_row(uiquery, number)
120
+ views_touched=map(uiquery, :scrollToRow, number)
121
+ if views_touched.empty? or views_touched.member? "<VOID>"
122
+ screenshot_and_raise "Unable to scroll: '#{uiquery}' to: #{number}"
123
+ end
124
+ views_touched
125
+ end
126
+
127
+ def scroll_to_cell(options={:query => "tableView",
128
+ :row => 0,
129
+ :section => 0,
130
+ :scroll_position => :top,
131
+ :animate => true})
132
+ uiquery = options[:query] || "tableView"
133
+ row = options[:row]
134
+ sec = options[:section]
135
+ if row.nil? or sec.nil?
136
+ raise "You must supply both :row and :section keys to scroll_to_cell"
137
+ end
138
+
139
+ args = []
140
+ if options.has_key?(:scroll_position)
141
+ args << options[:scroll_position]
142
+ else
143
+ args << "top"
144
+ end
145
+ if options.has_key?(:animate)
146
+ args << options[:animate]
147
+ end
148
+ views_touched=map(uiquery, :scrollToRow, row.to_i, sec.to_i, *args)
149
+
150
+ if views_touched.empty? or views_touched.member? "<VOID>"
151
+ screenshot_and_raise "Unable to scroll: '#{uiquery}' to: #{options}"
152
+ end
153
+ views_touched
154
+ end
155
+
156
+ def pinch(in_out, options={})
157
+ file = "pinch_in"
158
+ if in_out.to_sym==:out
159
+ file = "pinch_out"
160
+ end
161
+ playback(file, options)
162
+ end
163
+
164
+ #Current position of home button
165
+ def current_rotation
166
+ @current_rotation
167
+ end
168
+
169
+ def rotate(dir)
170
+ @current_rotation = @current_rotation || :down
171
+ rotate_cmd = nil
172
+ case dir
173
+ when :left then
174
+ if @current_rotation == :down
175
+ rotate_cmd = "left_home_down"
176
+ @current_rotation = :right
177
+ elsif @current_rotation == :right
178
+ rotate_cmd = "left_home_right"
179
+ @current_rotation = :up
180
+ elsif @current_rotation == :left
181
+ rotate_cmd = "left_home_left"
182
+ @current_rotation = :down
183
+ elsif @current_rotation == :up
184
+ rotate_cmd = "left_home_up"
185
+ @current_rotation = :left
186
+ end
187
+ when :right then
188
+ if @current_rotation == :down
189
+ rotate_cmd = "right_home_down"
190
+ @current_rotation = :left
191
+ elsif @current_rotation == :left
192
+ rotate_cmd = "right_home_left"
193
+ @current_rotation = :up
194
+ elsif @current_rotation == :right
195
+ rotate_cmd = "right_home_right"
196
+ @current_rotation = :down
197
+ elsif @current_rotation == :up
198
+ rotate_cmd = "right_home_up"
199
+ @current_rotation = :right
200
+ end
201
+ end
202
+
203
+ if rotate_cmd.nil?
204
+ screenshot_and_raise "Does not support rotating #{dir} when home button is pointing #{@current_rotation}"
205
+ end
206
+ playback("rotate_#{rotate_cmd}")
207
+ end
208
+
209
+ def background(secs)
210
+ set_user_pref("__calabash_action", {:action => :background, :duration => secs})
211
+ end
212
+
213
+ def prepare_dialog_action(opts={:dialog => nil, :answer => "Ok"})
214
+ if opts[:dialog].nil? || opts[:dialog].length < 1
215
+ raise ":dialog must be specified as a non-empty string (used as regexp to match dialog text)"
216
+ end
217
+ txt = opts[:answer] || 'Ok'
218
+ set_user_pref("__calabash_action", {:action => :dialog,
219
+ :text => opts[:dialog],
220
+ :answer => txt})
221
+ end
222
+
223
+ def move_wheel(opts={})
224
+ q = opts[:query] || "pickerView"
225
+ wheel = opts[:wheel] || 0
226
+ dir = opts[:dir] || :down
227
+
228
+ raise "Wheel index must be non negative" if wheel < 0
229
+ raise "Only up and down supported :dir (#{dir})" unless [:up, :down].include?(dir)
230
+
231
+ if ENV['OS'] == "ios4"
232
+ playback "wheel_#{dir}", :query => "#{q} pickerTable index:#{wheel}"
233
+ else
234
+ playback "wheel_#{dir}", :query => "#{q} pickerTableView index:#{wheel}"
235
+ end
236
+
237
+ end
238
+
239
+ def picker(opts={:query => "pickerView", :action => :texts})
240
+ raise "Not implemented" unless opts[:action] == :texts
241
+
242
+ q = opts[:query]
243
+
244
+ check_element_exists(q)
245
+
246
+ comps = query(q, :numberOfComponents).first
247
+ row_counts = []
248
+ texts = []
249
+ comps.times do |i|
250
+ row_counts[i] = query(q, :numberOfRowsInComponent => i).first
251
+ texts[i] = []
252
+ end
253
+
254
+ row_counts.each_with_index do |row_count, comp|
255
+ row_count.times do |i|
256
+ #view = query(q,[{:viewForRow => 0}, {:forComponent => 0}],:accessibilityLabel).first
257
+ spec = [{:viewForRow => i}, {:forComponent => comp}]
258
+ view = query(q, spec).first
259
+ if view
260
+ txt = query(q, spec, :accessibilityLabel).first
261
+ else
262
+ txt = query(q, :delegate, [{:pickerView => :view},
263
+ {:titleForRow => i},
264
+ {:forComponent => comp}]).first
265
+ end
266
+ texts[comp] << txt
267
+ end
268
+ end
269
+ texts
270
+ end
271
+
272
+ def recording_name_for(recording_name, os, device)
273
+ if !recording_name.end_with? ".base64"
274
+ "#{recording_name}_#{os}_#{device}.base64"
275
+ else
276
+ recording_name
277
+ end
278
+ end
279
+
280
+
281
+ def load_recording(recording,rec_dir)
282
+ data = nil
283
+ if (File.exists?(recording))
284
+ data = File.read(recording)
285
+ elsif (File.exists?("features/#{recording}"))
286
+ data = File.read("features/#{recording}")
287
+ elsif (File.exists?("#{rec_dir}/#{recording}"))
288
+ data = File.read("#{rec_dir}/#{recording}")
289
+ elsif (File.exists?("#{DATA_PATH}/resources/#{recording}"))
290
+ data = File.read("#{DATA_PATH}/resources/#{recording}")
291
+ end
292
+ data
293
+ end
294
+
295
+ def load_playback_data(recording_name, options={})
296
+ os = options["OS"] || ENV["OS"]
297
+ device = options["DEVICE"] || ENV["DEVICE"] || "iphone"
298
+
299
+ unless os
300
+ major = Calabash::Cucumber::SimulatorHelper.ios_major_version
301
+ unless major
302
+ raise <<EOF
303
+ Unable to determine iOS major version
304
+ Most likely you have updated your calabash-cucumber client
305
+ but not your server. Please follow closely:
306
+
307
+ https://github.com/calabash/calabash-ios/wiki/B1-Updating-your-Calabash-iOS-version
308
+
309
+ If you are running version 0.9.120+ then please report this message as a bug.
310
+ EOF
311
+ end
312
+ os = "ios#{major}"
313
+ end
314
+
315
+ rec_dir = ENV['PLAYBACK_DIR'] || "#{Dir.pwd}/playback"
316
+
317
+ recording = recording_name_for(recording_name, os, device)
318
+ data = load_recording(recording,rec_dir)
319
+
320
+ if data.nil? and os=="ios6"
321
+ recording = recording_name_for(recording_name, "ios5", device)
322
+ end
323
+
324
+ data = load_recording(recording,rec_dir)
325
+
326
+ if data.nil?
327
+ screenshot_and_raise "Playback not found: #{recording} (searched for #{recording} in #{Dir.pwd}, #{rec_dir}, #{DATA_PATH}/resources"
328
+ end
329
+
330
+ data
331
+ end
332
+
333
+ def playback(recording, options={})
334
+ data = load_playback_data(recording)
335
+
336
+ post_data = %Q|{"events":"#{data}"|
337
+ post_data<< %Q|,"query":"#{escape_quotes(options[:query])}"| if options[:query]
338
+ post_data<< %Q|,"offset":#{options[:offset].to_json}| if options[:offset]
339
+ post_data<< %Q|,"reverse":#{options[:reverse]}| if options[:reverse]
340
+ post_data<< %Q|,"prototype":"#{options[:prototype]}"| if options[:prototype]
341
+ post_data << "}"
342
+
343
+ res = http({:method => :post, :raw => true, :path => 'play'}, post_data)
344
+
345
+ res = JSON.parse(res)
346
+ if res['outcome'] != 'SUCCESS'
347
+ screenshot_and_raise "playback failed because: #{res['reason']}\n#{res['details']}"
348
+ end
349
+ res['results']
350
+ end
351
+
352
+ def interpolate(recording, options={})
353
+ data = load_playback_data(recording)
354
+
355
+ post_data = %Q|{"events":"#{data}"|
356
+ post_data<< %Q|,"start":"#{escape_quotes(options[:start])}"| if options[:start]
357
+ post_data<< %Q|,"end":"#{escape_quotes(options[:end])}"| if options[:end]
358
+ post_data<< %Q|,"offset_start":#{options[:offset_start].to_json}| if options[:offset_start]
359
+ post_data<< %Q|,"offset_end":#{options[:offset_end].to_json}| if options[:offset_end]
360
+ post_data << "}"
361
+
362
+ res = http({:method => :post, :raw => true, :path => 'interpolate'}, post_data)
363
+
364
+ res = JSON.parse(res)
365
+ if res['outcome'] != 'SUCCESS'
366
+ screenshot_and_raise "interpolate failed because: #{res['reason']}\n#{res['details']}"
367
+ end
368
+ res['results']
369
+ end
370
+
371
+ def record_begin
372
+ http({:method => :post, :path => 'record'}, {:action => :start})
373
+ end
374
+
375
+ def record_end(file_name)
376
+ res = http({:method => :post, :path => 'record'}, {:action => :stop})
377
+ File.open("_recording.plist", 'wb') do |f|
378
+ f.write res
379
+ end
380
+
381
+ device = ENV['DEVICE'] || 'iphone'
382
+ os = ENV['OS']
383
+
384
+ unless os
385
+ major = Calabash::Cucumber::SimulatorHelper.ios_major_version
386
+ unless major
387
+ raise <<EOF
388
+ Unable to determine iOS major version
389
+ Most likely you have updated your calabash-cucumber client
390
+ but not your server. Please follow closely:
391
+
392
+ https://github.com/calabash/calabash-ios/wiki/B1-Updating-your-Calabash-iOS-version
393
+
394
+ If you are running version 0.9.120+ then please report this message as a bug.
395
+ EOF
396
+ end
397
+ os = "ios#{major}"
398
+ end
399
+
400
+ file_name = "#{file_name}_#{os}_#{device}.base64"
401
+ system("/usr/bin/plutil -convert binary1 -o _recording_binary.plist _recording.plist")
402
+ system("openssl base64 -in _recording_binary.plist -out #{file_name}")
403
+ system("rm _recording.plist _recording_binary.plist")
404
+ file_name
405
+ end
406
+
407
+ def backdoor(sel, arg)
408
+ json = {
409
+ :selector => sel,
410
+ :arg => arg
411
+ }
412
+ res = http({:method => :post, :path => 'backdoor'}, json)
413
+ res = JSON.parse(res)
414
+ if res['outcome'] != 'SUCCESS'
415
+ screenshot_and_raise "backdoor #{json} failed because: #{res['reason']}\n#{res['details']}"
416
+ end
417
+ res['result']
418
+ end
419
+
420
+ def calabash_exit
421
+ # Exiting the app shuts down the HTTP connection and generates ECONNREFUSED,
422
+ # which needs to be suppressed.
423
+ begin
424
+ http(:path => 'exit', :retryable_errors => RETRYABLE_ERRORS - [Errno::ECONNREFUSED])
425
+ rescue Errno::ECONNREFUSED
426
+ []
427
+ end
428
+ end
429
+
430
+ def map(query, method_name, *method_args)
431
+ operation_map = {
432
+ :method_name => method_name,
433
+ :arguments => method_args
434
+ }
435
+ res = http({:method => :post, :path => 'map'},
436
+ {:query => query, :operation => operation_map})
437
+ res = JSON.parse(res)
438
+ if res['outcome'] != 'SUCCESS'
439
+ screenshot_and_raise "map #{query}, #{method_name} failed because: #{res['reason']}\n#{res['details']}"
440
+ end
441
+
442
+ res['results']
443
+ end
444
+
445
+
446
+ def start_app_in_background(path=nil, sdk = nil, version = 'iphone', args = nil)
447
+
448
+ if path.nil?
449
+ path = ENV['APP_BUNDLE_PATH'] || (defined?(APP_BUNDLE_PATH) && APP_BUNDLE_PATH)
450
+ end
451
+ app_bundle_path = Calabash::Cucumber::SimulatorHelper.app_bundle_or_raise(path)
452
+
453
+ @ios_device = RunLoop.run(:app => app_bundle_path)
454
+ end
455
+
456
+ def send_uia_command(opts ={})
457
+ RunLoop.send_command(opts[:device] ||@ios_device, opts[:command])
458
+ end
459
+
460
+ def stop_background_app(stop_spec = nil)
461
+
462
+ @ios_device = RunLoop.stop(stop_spec || @ios_device)
463
+
464
+ end
465
+
466
+
467
+
468
+ def http(options, data=nil)
469
+ options[:uri] = url_for(options[:path])
470
+ options[:method] = options[:method] || :get
471
+ if data
472
+ if options[:raw]
473
+ options[:body] = data
474
+ else
475
+ options[:body] = data.to_json
476
+ end
477
+ end
478
+ res = make_http_request(options)
479
+ res.force_encoding("UTF-8") if res.respond_to?(:force_encoding)
480
+ res
481
+ end
482
+
483
+
484
+ def url_for(verb)
485
+ url = URI.parse(ENV['DEVICE_ENDPOINT']|| "http://localhost:37265")
486
+ path = url.path
487
+ if path.end_with? "/"
488
+ path = "#{path}#{verb}"
489
+ else
490
+ path = "#{path}/#{verb}"
491
+ end
492
+ url.path = path
493
+ url
494
+ end
495
+
496
+ def make_http_request(options)
497
+ body = nil
498
+ retryable_errors = options[:retryable_errors] || RETRYABLE_ERRORS
499
+ CAL_HTTP_RETRY_COUNT.times do |count|
500
+ begin
501
+ if not @http
502
+ @http = init_request(options)
503
+ end
504
+ if options[:method] == :post
505
+ body = @http.post(options[:uri], options[:body]).body
506
+ else
507
+ body = @http.get(options[:uri], options[:body]).body
508
+ end
509
+ break
510
+ rescue HTTPClient::TimeoutError => e
511
+ if count < CAL_HTTP_RETRY_COUNT-1
512
+ sleep(0.5)
513
+ @http.reset_all
514
+ @http=nil
515
+ STDOUT.write "Retrying.. #{e.class}: (#{e})\n"
516
+ STDOUT.flush
517
+
518
+ else
519
+ puts "Failing... #{e.class}"
520
+ raise e
521
+ end
522
+ rescue Exception => e
523
+ if retryable_errors.include?(e)
524
+ if count < CAL_HTTP_RETRY_COUNT-1
525
+ sleep(0.5)
526
+ @http.reset_all
527
+ @http=nil
528
+ STDOUT.write "Retrying.. #{e.class}: (#{e})\n"
529
+ STDOUT.flush
530
+
531
+ else
532
+ puts "Failing... #{e.class}"
533
+ raise e
534
+ end
535
+ else
536
+ raise e
537
+ end
538
+ end
539
+ end
540
+
541
+ body
542
+ end
543
+
544
+ def init_request(url)
545
+ http = HTTPClient.new
546
+ http.connect_timeout = 15
547
+ http.send_timeout = 15
548
+ http.receive_timeout = 15
549
+ http
550
+ end
551
+ end
552
+ end
553
+ end