bubble-wrap 1.2.0 → 1.3.0.osx

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +4 -2
  3. data/Gemfile.lock +1 -1
  4. data/README.md +217 -7
  5. data/Rakefile +23 -2
  6. data/lib/bubble-wrap/camera.rb +10 -6
  7. data/lib/bubble-wrap/core.rb +14 -1
  8. data/lib/bubble-wrap/ext/motion_project_app.rb +8 -0
  9. data/lib/bubble-wrap/font.rb +3 -1
  10. data/lib/bubble-wrap/http.rb +2 -0
  11. data/lib/bubble-wrap/loader.rb +17 -2
  12. data/lib/bubble-wrap/location.rb +9 -6
  13. data/lib/bubble-wrap/media.rb +10 -6
  14. data/lib/bubble-wrap/test.rb +6 -1
  15. data/lib/bubble-wrap/ui.rb +5 -2
  16. data/lib/bubble-wrap/version.rb +2 -7
  17. data/motion/core.rb +6 -1
  18. data/motion/core/app.rb +3 -64
  19. data/motion/core/device.rb +0 -55
  20. data/motion/core/device/{camera.rb → ios/camera.rb} +0 -0
  21. data/motion/core/device/{camera_wrapper.rb → ios/camera_wrapper.rb} +0 -0
  22. data/motion/core/device/ios/screen.rb +75 -0
  23. data/motion/core/device/osx/screen.rb +18 -0
  24. data/motion/core/device/screen.rb +1 -69
  25. data/motion/core/ios/app.rb +71 -0
  26. data/motion/core/ios/device.rb +59 -0
  27. data/motion/core/osx/app.rb +15 -0
  28. data/motion/core/osx/device.rb +6 -0
  29. data/motion/core/string.rb +3 -2
  30. data/motion/http.rb +0 -364
  31. data/motion/http/query.rb +367 -0
  32. data/motion/http/response.rb +32 -0
  33. data/motion/test_suite_delegate.rb +58 -0
  34. data/motion/ui/ui_alert_view.rb +169 -0
  35. data/motion/ui/ui_bar_button_item.rb +55 -53
  36. data/motion/util/constants.rb +34 -32
  37. data/samples/alert/.gitignore +16 -0
  38. data/samples/alert/Gemfile +3 -0
  39. data/samples/alert/Rakefile +10 -0
  40. data/samples/alert/app/app_delegate.rb +8 -0
  41. data/samples/alert/app/controllers/alert_view_controller.rb +74 -0
  42. data/samples/alert/resources/Default-568h@2x.png +0 -0
  43. data/samples/alert/spec/main_spec.rb +9 -0
  44. data/samples/media/.gitignore +16 -0
  45. data/samples/media/Rakefile +11 -0
  46. data/samples/media/app/app_delegate.rb +8 -0
  47. data/samples/media/app/controllers/play_controller.rb +46 -0
  48. data/samples/media/resources/Default-568h@2x.png +0 -0
  49. data/samples/media/resources/test.mp3 +0 -0
  50. data/samples/media/spec/main_spec.rb +9 -0
  51. data/samples/osx/Gemfile +3 -0
  52. data/samples/osx/Gemfile.lock +10 -0
  53. data/samples/osx/Rakefile +11 -0
  54. data/samples/osx/app/app_delegate.rb +69 -0
  55. data/samples/osx/app/menu.rb +108 -0
  56. data/samples/osx/resources/Credits.rtf +29 -0
  57. data/samples/osx/spec/main_spec.rb +9 -0
  58. data/spec/motion/core/app_spec.rb +5 -164
  59. data/spec/motion/core/device/{camera_spec.rb → ios/camera_spec.rb} +0 -0
  60. data/spec/motion/core/device/{camera_wrapper_spec.rb → ios/camera_wrapper_spec.rb} +0 -0
  61. data/spec/motion/core/device/ios/device_spec.rb +74 -0
  62. data/spec/motion/core/device/{screen_spec.rb → ios/screen_spec.rb} +2 -1
  63. data/spec/motion/core/device/osx/screen_spec.rb +26 -0
  64. data/spec/motion/core/device_spec.rb +0 -71
  65. data/spec/motion/core/ios/app_spec.rb +180 -0
  66. data/spec/motion/core/kvo_spec.rb +23 -7
  67. data/spec/motion/core/ns_index_path_spec.rb +10 -2
  68. data/spec/motion/core/osx/app_spec.rb +15 -0
  69. data/spec/motion/core/string_spec.rb +11 -5
  70. data/spec/motion/core_spec.rb +13 -2
  71. data/spec/motion/http/query_spec.rb +731 -0
  72. data/spec/motion/http/response_spec.rb +44 -0
  73. data/spec/motion/http_spec.rb +0 -722
  74. data/spec/motion/{core → ui}/gestures_spec.rb +0 -0
  75. data/spec/motion/ui/ui_alert_view_spec.rb +1188 -0
  76. data/spec/motion/{core → ui}/ui_bar_button_item_spec.rb +80 -24
  77. data/spec/motion/{core → ui}/ui_control_spec.rb +0 -0
  78. data/spec/motion/util/constants_spec.rb +4 -4
  79. metadata +86 -26
@@ -1,7 +1,11 @@
1
1
  describe "NSIndexPathWrap" do
2
2
 
3
3
  before do
4
- @index = NSIndexPath.indexPathForRow(0, inSection:3)
4
+ if App.osx?
5
+ @index = NSIndexPath.indexPathWithIndex(3)
6
+ else
7
+ @index = NSIndexPath.indexPathForRow(0, inSection:3)
8
+ end
5
9
  end
6
10
 
7
11
  it "should be able to use an array like accessor" do
@@ -13,7 +17,11 @@ describe "NSIndexPathWrap" do
13
17
  @index.each do |idx|
14
18
  i << idx
15
19
  end
16
- i.should == [3, 0]
20
+ if App.osx?
21
+ i.should == [3]
22
+ else
23
+ i.should == [3, 0]
24
+ end
17
25
  end
18
26
 
19
27
  end
@@ -0,0 +1,15 @@
1
+ describe BubbleWrap::App do
2
+ describe "OS X" do
3
+ describe '.delegate' do
4
+ it 'returns a TestSuiteDelegate' do
5
+ App.delegate.should == NSApplication.sharedApplication.delegate
6
+ end
7
+ end
8
+
9
+ describe '.shared' do
10
+ it 'returns UIApplication.sharedApplication' do
11
+ App.shared.should == NSApplication.sharedApplication
12
+ end
13
+ end
14
+ end
15
+ end
@@ -92,8 +92,10 @@ describe BubbleWrap::String do
92
92
  end
93
93
 
94
94
  before do
95
- @blue_color = UIColor.blueColor
96
- @orange_color = UIColor.colorWithRed((255.0/255.0), green:(138.0/255.0), blue:(25.0/255.0), alpha:1.0)
95
+ @blue_color = App.osx? ? NSColor.colorWithDeviceRed(0,green:0,blue:1,alpha:1) : UIColor.blueColor
96
+ r,g,b,a = [1, (138.0/255.0), (25.0/255.0), 1]
97
+ @orange_color = App.osx? ? NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: a) :
98
+ UIColor.colorWithRed(r, green:g, blue:b, alpha:a)
97
99
  end
98
100
 
99
101
  describe "A UIColor should be created from a String with a hex color" do
@@ -116,15 +118,19 @@ describe BubbleWrap::String do
116
118
 
117
119
  describe "a string with a color keyword (blue, red, lightText)" do
118
120
  it "should return the corresponding color" do
119
- 'blue'.to_color.should == UIColor.blueColor
121
+ 'blue'.to_color.should == (App.osx? ? NSColor.blueColor : UIColor.blueColor)
120
122
  end
121
123
 
122
124
  it "should accept camelCase" do
123
- 'lightText'.to_color.should == UIColor.lightTextColor
125
+ if App.osx?
126
+ 'headerText'.to_color.should == NSColor.headerTextColor
127
+ else
128
+ 'lightText'.to_color.should == UIColor.lightTextColor
129
+ end
124
130
  end
125
131
 
126
132
  it "should accept snake_case" do
127
- 'dark_gray'.to_color.should == UIColor.darkGrayColor
133
+ 'dark_gray'.to_color.should == (App.osx? ? NSColor.darkGrayColor : UIColor.darkGrayColor)
128
134
  end
129
135
  end
130
136
 
@@ -28,13 +28,24 @@ describe 'BubbleWrap' do
28
28
  end
29
29
 
30
30
  it "creates color with rgb devided by 255 with alpha=1" do
31
- color = UIColor.colorWithRed((@red/255.0), green:(@green/255.0), blue:(@blue/255.0), alpha:1)
31
+ color = nil
32
+ r,g,b,a = [(@red/255.0), (@green/255.0), (@blue/255.0), 1]
33
+ if App.osx?
34
+ color = NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: a)
35
+ else
36
+ color = UIColor.colorWithRed(r, green:g, blue:b, alpha:a)
37
+ end
32
38
  BubbleWrap::rgb_color(@red, @green, @blue).should.equal color
33
39
  end
34
40
 
35
41
  it "rgba_color uses the real alpha" do
36
42
  alpha = 0.4
37
- color = UIColor.colorWithRed((@red/255.0), green:(@green/255.0), blue:(@blue/255.0), alpha:alpha)
43
+ r,g,b,a = [(@red/255.0), (@green/255.0), (@blue/255.0), alpha]
44
+ if App.osx?
45
+ color = NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: a)
46
+ else
47
+ color = UIColor.colorWithRed(r, green:g, blue:b, alpha:a)
48
+ end
38
49
  BubbleWrap::rgba_color(@red, @green, @blue, alpha).should.equal color
39
50
  end
40
51
 
@@ -0,0 +1,731 @@
1
+ describe BubbleWrap::HTTP::Query do
2
+
3
+ before do
4
+ @localhost_url = 'http://localhost'
5
+ @fake_url = 'http://fake.url'
6
+
7
+ @credentials = { username: 'mneorr', password: '123456xx!@crazy' }
8
+ @credential_persistence = 0
9
+ @payload = {
10
+ user: { name: 'marin', surname: 'usalj' },
11
+ twitter: '@mneorr',
12
+ website: 'mneorr.com',
13
+ values: ['apple', 'orange', 'peach'],
14
+ credentials: @credentials
15
+ }
16
+ @action = Proc.new { |response| @real_response = response; @delegator_was_called = true }
17
+ @format = "application/x-www-form-urlencoded"
18
+ @cache_policy = 24234
19
+ @leftover_option = 'trololo'
20
+ @headers = { 'User-Agent' => "Mozilla/5.0 (X11; Linux x86_64; rv:12.0) \n Gecko/20100101 Firefox/12.0" }
21
+ @files = {
22
+ fake_file: NSJSONSerialization.dataWithJSONObject({ fake: 'json' }, options:0, error:nil),
23
+ empty_file: NSMutableData.data
24
+ }
25
+ @options = {
26
+ action: @action,
27
+ files: @files,
28
+ payload: @payload,
29
+ credentials: @credentials,
30
+ credential_persistence: @credential_persistence,
31
+ headers: @headers,
32
+ cache_policy: @cache_policy,
33
+ leftover_option: @leftover_option,
34
+ format: @format
35
+ }
36
+ @query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, @options )
37
+ end
38
+
39
+ it "has appropriate attributes" do
40
+ @query.should.respond_to :request=
41
+ @query.should.respond_to :connection=
42
+ @query.should.respond_to :credentials=
43
+ @query.should.respond_to :proxy_credential=
44
+ @query.should.respond_to :post_data=
45
+
46
+ @query.should.respond_to :method
47
+ @query.should.respond_to :response
48
+ @query.should.respond_to :status_code
49
+ @query.should.respond_to :response_headers
50
+ @query.should.respond_to :response_size
51
+ @query.should.respond_to :options
52
+ end
53
+
54
+ it "should accept nil header value" do
55
+ @headers = { 'Authorization' => nil, 'User-Agent' => "Mozilla/5.0 (X11; Linux x86_64; rv:12.0) \n Gecko/20100101 Firefox/12.0" }
56
+ @options = {
57
+ headers: @headers,
58
+ }
59
+ query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, @options )
60
+ query.should.not.be.nil
61
+ end
62
+
63
+ describe "When initialized" do
64
+
65
+ it "should upcase the HTTP method" do
66
+ @query.method.should.equal "GET"
67
+ end
68
+
69
+ it "throws an error for invalid/missing URL schemes" do
70
+ %w(http https file ftp).each do |scheme|
71
+ lambda {
72
+ BW::HTTP::Query.new("#{scheme}://example.com", :get) { |r| p r.body.to_str }
73
+ }.should.not.raise InvalidURLError
74
+ end
75
+
76
+ lambda {
77
+ BW::HTTP::Query.new("bad://example.com", :get) { |r| p r.body.to_str }
78
+ }.should.raise InvalidURLError
79
+ end
80
+
81
+ it "should set the deleted delegator from options" do
82
+ @query.instance_variable_get(:@delegator).should.equal @action
83
+ @options.should.not.has_key? :action
84
+ end
85
+
86
+ it "sets the files to instance variable" do
87
+ @query.instance_variable_get(:@files).should.equal @files
88
+ @options.should.not.has_key? :files
89
+ end
90
+
91
+ it "sets the format from options" do
92
+ @query.instance_variable_get(:@format).should.equal @format
93
+ @options.should.not.has_key? :format
94
+ end
95
+
96
+ it "should set self as the delegator if action was not passed in" do
97
+ new_query = BubbleWrap::HTTP::Query.new( 'http://localhost', :get, {})
98
+ new_query.instance_variable_get(:@delegator).should.equal new_query
99
+ end
100
+
101
+ it "should merge :username and :password in loaded credentials" do
102
+ @query.credentials.should.equal @credentials
103
+
104
+ options = { credentials: {} }
105
+ new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, options)
106
+
107
+ generated_credentials = { :username => nil, :password => nil }
108
+ new_query.credentials.should.equal generated_credentials
109
+ options.should.be.empty
110
+ end
111
+
112
+
113
+ describe "PAYLOAD / UPLOAD FILES" do
114
+
115
+ def create_query(payload, files)
116
+ BubbleWrap::HTTP::Query.new( 'http://haha', :post, { payload: payload, files: files } )
117
+ end
118
+
119
+ def sample_data
120
+ "twitter:@mneorr".dataUsingEncoding NSUTF8StringEncoding
121
+ end
122
+
123
+ it "should set payload from options{} to @payload" do
124
+ payload = "user%5Bname%5D=marin&user%5Bsurname%5D=usalj&twitter=%40mneorr&website=mneorr.com&values%5B%5D=apple&values%5B%5D=orange&values%5B%5D=peach&credentials%5Busername%5D=mneorr&credentials%5Bpassword%5D=123456xx%21%40crazy"
125
+ @query.instance_variable_get(:@payload).should.equal payload
126
+ @options.should.not.has_key? :payload
127
+ end
128
+
129
+ it "should check if @payload is a hash before generating GET params" do
130
+ query_string_payload = BubbleWrap::HTTP::Query.new( @fake_url , :get, { payload: "name=apple&model=macbook"} )
131
+ query_string_payload.instance_variable_get(:@payload).should.equal 'name=apple&model=macbook'
132
+ end
133
+
134
+ it "should check if payload is nil" do
135
+ lambda{
136
+ BubbleWrap::HTTP::Query.new( @fake_url , :post, {} )
137
+ }.should.not.raise NoMethodError
138
+ end
139
+
140
+ it "should set the payload in URL only for GET and HEAD requests" do
141
+ [:post, :put, :delete, :patch].each do |method|
142
+ query = BubbleWrap::HTTP::Query.new( @localhost_url , method, { payload: @payload } )
143
+ query.instance_variable_get(:@url).description.should.equal @localhost_url
144
+ end
145
+
146
+ payload = {name: 'marin'}
147
+ [:get, :head].each do |method|
148
+ query = BubbleWrap::HTTP::Query.new( @localhost_url , method, { payload: payload } )
149
+ query.instance_variable_get(:@url).description.should.equal "#{@localhost_url}?name=marin"
150
+ end
151
+ end
152
+
153
+ it "processes filenames from file hashes" do
154
+ files = {
155
+ upload: {data: sample_data, filename: "test.txt"}
156
+ }
157
+ query = BubbleWrap::HTTP::Query.new(@fake_url, :post, {files: files})
158
+ uuid = query.instance_variable_get(:@boundary)
159
+ real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding)
160
+ real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"test.txt\"\r\nContent-Type: application/octet-stream\r\n\r\ntwitter:@mneorr\r\n--#{uuid}--\r\n"
161
+ end
162
+
163
+ it "processes filenames from file hashes, using the name when the filename is missing" do
164
+ files = {
165
+ upload: {data: sample_data}
166
+ }
167
+ query = BubbleWrap::HTTP::Query.new(@fake_url, :post, {files: files})
168
+ uuid = query.instance_variable_get(:@boundary)
169
+ real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding)
170
+ real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"upload\"\r\nContent-Type: application/octet-stream\r\n\r\ntwitter:@mneorr\r\n--#{uuid}--\r\n"
171
+ end
172
+
173
+ it "throws an error for invalid file parameters" do
174
+ files = {
175
+ twitter: {filename: "test.txt", data: nil}
176
+ }
177
+ lambda {
178
+ BW::HTTP::Query.new("http://example.com", :post, { files: files})
179
+ }.should.raise InvalidFileError
180
+ end
181
+
182
+ it "sets the HTTPBody DATA to @request for all methods except GET and HEAD" do
183
+ payload = { name: 'apple', model: 'macbook'}
184
+ files = { twitter: sample_data, site: "mneorr.com".dataUsingEncoding(NSUTF8StringEncoding) }
185
+
186
+ puts "\n"
187
+ [:post, :put, :delete, :patch].each do |method|
188
+ puts " - #{method}\n"
189
+ query = BubbleWrap::HTTP::Query.new( @fake_url , method, { payload: payload, files: files } )
190
+ uuid = query.instance_variable_get(:@boundary)
191
+ real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding)
192
+ real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\napple\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nmacbook\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"twitter\"; filename=\"twitter\"\r\nContent-Type: application/octet-stream\r\n\r\ntwitter:@mneorr\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"site\"; filename=\"site\"\r\nContent-Type: application/octet-stream\r\n\r\nmneorr.com\r\n--#{uuid}--\r\n"
193
+ end
194
+
195
+ [:get, :head].each do |method|
196
+ puts " - #{method}\n"
197
+ query = BubbleWrap::HTTP::Query.new( @fake_url , method, { payload: payload } )
198
+ real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding)
199
+ real_payload.should.be.empty
200
+ end
201
+ end
202
+
203
+ it "sets the payload without conversion to-from NSString if the payload was NSData" do
204
+ data = sample_data
205
+ lambda { query = create_query(data, nil) }.should.not.raise NoMethodError
206
+ end
207
+
208
+ it "sets the payload as a string if JSON" do
209
+ json = "{\"foo\":42,\"bar\":\"BubbleWrap\"}"
210
+ puts "\n"
211
+ [:put, :post, :delete, :patch].each do |method|
212
+ puts " - #{method}\n"
213
+ query = BubbleWrap::HTTP::Query.new( @fake_url , method, { payload: json } )
214
+ set_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding)
215
+ set_payload.should.equal json
216
+ end
217
+ end
218
+
219
+ it "sets the payload for a nested hash to multiple form-data parts" do
220
+ payload = { computer: { name: 'apple', model: 'macbook'} }
221
+ query = BubbleWrap::HTTP::Query.new( @fake_url, :post, { payload: payload } )
222
+ uuid = query.instance_variable_get(:@boundary)
223
+ real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding)
224
+ real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"computer[name]\"\r\n\r\napple\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"computer[model]\"\r\n\r\nmacbook\r\n--#{uuid}--\r\n"
225
+ end
226
+
227
+ end
228
+
229
+ it "should set default timeout to 30s or the one from hash" do
230
+ @query.instance_variable_get(:@timeout).should == 30
231
+
232
+ options = {timeout: 10}
233
+ new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, options)
234
+
235
+ new_query.instance_variable_get(:@timeout).should == 10
236
+ options.should.be.empty
237
+ end
238
+
239
+ it "should delete :headers from options and escape Line Feeds" do
240
+ escaped_lf = "Mozilla/5.0 (X11; Linux x86_64; rv:12.0) \r\n Gecko/20100101 Firefox/12.0"
241
+ headers = @query.instance_variable_get(:@headers)
242
+
243
+ headers["User-Agent"].should.equal escaped_lf
244
+ end
245
+
246
+ it "should delete :cache_policy or set NSURLRequestUseProtocolCachePolicy" do
247
+ @query.instance_variable_get(:@cache_policy).should.equal @cache_policy
248
+ @options.should.not.has_key? :cache_policy
249
+
250
+ new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, {})
251
+ new_query.instance_variable_get(:@cache_policy).should.equal NSURLRequestUseProtocolCachePolicy
252
+ end
253
+
254
+ it "should delete :credential_persistence or set NSURLCredentialPersistenceForSession" do
255
+ @query.instance_variable_get(:@credential_persistence).should.equal @credential_persistence
256
+ @options.should.not.has_key? :credential_persistence
257
+
258
+ new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, {})
259
+ new_query.instance_variable_get(:@credential_persistence).should.equal NSURLCredentialPersistenceForSession
260
+ end
261
+
262
+ it "should present base64-encoded credentials in Authorization header when provided" do
263
+ headers = @query.instance_variable_get(:@headers)
264
+
265
+ headers["Authorization"].should.equal "Basic bW5lb3JyOjEyMzQ1Nnh4IUBjcmF6eQ=="
266
+ end
267
+
268
+ it "should not present Authorization header when :present_credentials is false" do
269
+ query = BubbleWrap::HTTP::Query.new(@fake_url, :get, { credentials: @credentials, present_credentials: false })
270
+ headers = query.instance_variable_get(:@headers)
271
+
272
+ headers.should.equal nil
273
+ end
274
+
275
+
276
+ it "should set the rest of options{} to ivar @options" do
277
+ @query.options.size.should.equal 1
278
+ @query.options.values[0].should.equal @leftover_option
279
+ end
280
+
281
+ it "should create a new response before instantiating a new request" do
282
+ @query.response.should.not.equal nil
283
+ end
284
+
285
+ it "should call initiate_request with the URL passed in" do
286
+ processed_url = "http://localhost?user%5Bname%5D=marin&user%5Bsurname%5D=usalj&twitter=%40mneorr&website=mneorr.com&values%5B%5D=apple&values%5B%5D=orange&values%5B%5D=peach&credentials%5Busername%5D=mneorr&credentials%5Bpassword%5D=123456xx%21%40crazy"
287
+ @query.instance_variable_get(:@url).description.should.equal processed_url
288
+ end
289
+
290
+ it "should pass the new request in the new connection" do
291
+ @query.connection.request.URL.description.should.equal @query.request.URL.description
292
+ end
293
+
294
+ it "should start the connection" do
295
+ @query.connection.was_started.should.equal true
296
+ end
297
+
298
+ if !App.osx?
299
+ it "should turn on the network indicator" do
300
+ UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should.equal true
301
+ end
302
+ end
303
+ end
304
+
305
+ describe "create request" do
306
+
307
+ before do
308
+ @url_string = 'http://initiated-request.dev/to convert'
309
+ @headers = { fake: 'headers' }
310
+ @get_query = BubbleWrap::HTTP::Query.new( @url_string , :get, { headers: @headers } )
311
+ end
312
+
313
+ it "should create a new request with HTTP method & header fields" do
314
+ @query.request.HTTPMethod.should.equal @query.method
315
+ @get_query.request.allHTTPHeaderFields.should.equal @headers
316
+ end
317
+
318
+ it "creates a new NSURLConnection and sets itself as a delegate" do
319
+ @query.connection.delegate.should.equal @query
320
+ end
321
+
322
+ it "should patch the NSURLRequest with done_loading and done_loading!" do
323
+ @query.request.done_loading?.should.equal @query.request.instance_variable_get(:@done_loading)
324
+
325
+ @query.request.instance_variable_set(:@done_loading, false)
326
+ @query.request.done_loading?.should.equal false
327
+ @query.request.done_loading!
328
+ @query.request.done_loading?.should.equal true
329
+ end
330
+
331
+ it "should pass the right arguments when creating new request" do
332
+ @query.request.cachePolicy.should.equal @query.instance_variable_get(:@cache_policy)
333
+ @query.request.timeoutInterval.should.equal @query.instance_variable_get(:@timeout)
334
+ end
335
+
336
+ end
337
+
338
+ describe "create POST request" do
339
+
340
+ before do
341
+ @url_string = 'http://initiated-request.dev/post'
342
+ @headers = { fake: 'headers' }
343
+ @payload = { key:'abc1234' }
344
+ @post_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: @payload})
345
+ end
346
+
347
+ it "should add default Content Type if no payload is given" do
348
+ query_without_payload = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers})
349
+ query_without_payload.request.allHTTPHeaderFields.should.include? 'Content-Type'
350
+ end
351
+
352
+ it "should automatically provide Content-Type if a payload is provided" do
353
+ @post_query.request.allHTTPHeaderFields.should.include?('Content-Type')
354
+ end
355
+
356
+ it "should use the format parameter to decide the Content-Type" do
357
+ json_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, format: :json, payload: "{\"key\":\"abc1234\"}"})
358
+ json_query.request.allHTTPHeaderFields['Content-Type'].should.equal "application/json"
359
+ end
360
+
361
+ it "should default to multipart/form-data for payloads with a hash" do
362
+ uuid = @post_query.instance_variable_get(:@boundary)
363
+ @post_query.request.allHTTPHeaderFields['Content-Type'].should.equal "multipart/form-data; boundary=#{uuid}"
364
+ end
365
+
366
+ it "should default to application/x-www-form-urlencoded for non-hash payloads" do
367
+ string_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: "{\"key\":\"abc1234\"}"})
368
+ string_query.request.allHTTPHeaderFields['Content-Type'].should.equal "application/x-www-form-urlencoded"
369
+ end
370
+
371
+ it "should not add Content-Type if you provide one yourself" do
372
+ # also ensures check is case insenstive
373
+ @headers = { fake: 'headers', 'CONTENT-TYPE' => 'x-banana' }
374
+ @post_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: @payload})
375
+ @post_query.request.allHTTPHeaderFields['CONTENT-TYPE'].should.equal @headers['CONTENT-TYPE']
376
+ end
377
+
378
+ end
379
+
380
+ describe "Generating payloads" do
381
+
382
+ it "should create payload key/value pairs from nested hashes with prefix[key]=value" do
383
+ expected_params = [
384
+ ['user[name]', 'marin'],
385
+ ['user[surname]', 'usalj'],
386
+ ['twitter', '@mneorr'],
387
+ ['website', 'mneorr.com'],
388
+ ['values[]', 'apple'],
389
+ ['values[]', 'orange'],
390
+ ['values[]', 'peach'],
391
+ ['credentials[username]', 'mneorr'],
392
+ ['credentials[password]', '123456xx!@crazy']
393
+ ]
394
+ @query.send(:process_payload_hash, @payload).should.equal expected_params
395
+ end
396
+
397
+ it "should create payload key/value pairs from nested arrays of hashes with prefix[key][][nested_key]=value" do
398
+ payload = {
399
+ user: {
400
+ phones_attributes: [
401
+ { number: 1234567, area_code: 213 },
402
+ { number: 7654321, area_code: 310 }
403
+ ]
404
+ }
405
+ }
406
+ expected_params = [
407
+ ['user[phones_attributes][][number]', 1234567],
408
+ ['user[phones_attributes][][area_code]', 213],
409
+ ['user[phones_attributes][][number]', 7654321],
410
+ ['user[phones_attributes][][area_code]', 310]
411
+ ]
412
+ @query.send(:process_payload_hash, payload).should.equal expected_params
413
+ end
414
+ end
415
+
416
+ describe "when didReceiveResponse:" do
417
+
418
+ it "should assign status_code, headers and response_size" do
419
+ headers = { foo: 'bar' }
420
+ status_code = 234
421
+ length = 123.53
422
+
423
+ response = FakeURLResponse.new(status_code, headers, length)
424
+ @query.connection(nil, didReceiveResponse:response)
425
+
426
+ @query.status_code.should.equal status_code
427
+ @query.response_headers.should.equal headers
428
+ @query.response_size.should.equal length
429
+ end
430
+
431
+ end
432
+
433
+ describe "when didRecieveData:" do
434
+
435
+ def query_received_data
436
+ @query.instance_variable_get(:@received_data)
437
+ end
438
+
439
+ it "should initialize @received_data and append the received data" do
440
+ query_received_data.should.equal nil
441
+ data = NSData.dataWithBytesNoCopy(Pointer.new(:char, 'abc'), length:24, freeWhenDone: false)
442
+
443
+ @query.connection(nil, didReceiveData:nil)
444
+ query_received_data.should.not.equal nil
445
+
446
+ @query.connection(nil, didReceiveData:data)
447
+ query_received_data.length.should.equal 24
448
+
449
+ @query.connection(nil, didReceiveData:data)
450
+ query_received_data.length.should.equal 48
451
+ end
452
+
453
+ end
454
+
455
+
456
+
457
+ describe "when requestDidFailWithError:" do
458
+ before do
459
+ @fake_error = NSError.errorWithDomain('testing', code:7768, userInfo:nil)
460
+ end
461
+
462
+ if !App.osx?
463
+ it "should turn off network indicator" do
464
+ UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == true
465
+ @query.connection(nil, didFailWithError:@fake_error)
466
+ UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == false
467
+ end
468
+ end
469
+
470
+ it "should set request_done to true" do
471
+ @query.request.done_loading?.should == false
472
+ @query.connection(nil, didFailWithError:@fake_error)
473
+ @query.request.done_loading?.should == true
474
+ end
475
+
476
+ it "should set the error message to response object" do
477
+ @query.response.error_message.should.equal nil
478
+ @query.connection(nil, didFailWithError:@fake_error)
479
+ @query.response.error_message.should.equal @fake_error.localizedDescription
480
+ end
481
+
482
+ it "should check if there's a callback block and pass the response in" do
483
+ expected_response = BubbleWrap::HTTP::Response.new
484
+ real_response = nil
485
+ block = lambda{ |response, query| real_response = response }
486
+
487
+ query = BubbleWrap::HTTP::Query.new(@localhost_url, :get, { :action => block })
488
+ query.instance_variable_set(:@response, expected_response)
489
+
490
+ query.connection(nil, didFailWithError:@fake_error)
491
+ real_response.should.equal expected_response
492
+ end
493
+
494
+ end
495
+
496
+ describe "when connectionDidFinishLoading:" do
497
+
498
+ if !App.osx?
499
+ it "should turn off the network indicator" do
500
+ UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == true
501
+
502
+ @query.connectionDidFinishLoading(nil)
503
+ UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == false
504
+ end
505
+ end
506
+
507
+ it "should set request_done to true" do
508
+ @query.request.done_loading?.should == false
509
+
510
+ @query.connectionDidFinishLoading(nil)
511
+ @query.request.done_loading?.should == true
512
+ end
513
+
514
+ it "should set response_body to @received data if not nil" do
515
+ data = NSData.dataWithBytesNoCopy(Pointer.new(:char, 'abc'), length:24, freeWhenDone: false)
516
+ headers = { foo: 'bar' }
517
+ status_code = 234
518
+ response = FakeURLResponse.new(status_code, headers, 65456)
519
+
520
+ @query.connection(nil, didReceiveResponse:response)
521
+ @query.connection(nil, didReceiveData:data)
522
+ @query.connectionDidFinishLoading(nil)
523
+
524
+ @query.response.body.should.equal data
525
+ @query.response.status_code.should.equal status_code
526
+ @query.response.headers.should.equal headers
527
+ @query.response.url.should.equal @query.instance_variable_get(:@url)
528
+ end
529
+
530
+ it "should check if there's a callback block and pass the response in" do
531
+ expected_response = BubbleWrap::HTTP::Response.new
532
+ real_response = nil
533
+ block = lambda{ |response, query| real_response = response }
534
+ query = BubbleWrap::HTTP::Query.new(@localhost_url, :get, { :action => block })
535
+ query.instance_variable_set(:@response, expected_response)
536
+
537
+ query.connectionDidFinishLoading(nil)
538
+ real_response.should.equal expected_response
539
+ end
540
+
541
+ end
542
+
543
+ describe "when connection:willSendRequest:redirectResponse:" do
544
+ before do
545
+ @request = NSMutableURLRequest.requestWithURL NSURL.URLWithString('http://fakehost.local/')
546
+ end
547
+
548
+ it "should forward the new request for 30 times/redirections" do
549
+ 1.upto(35) do |numbah|
550
+ request = @query.connection(nil, willSendRequest:@request, redirectResponse:nil)
551
+ request.should.equal numbah < 30 ? @request : nil
552
+ end
553
+ end
554
+
555
+ it "should always allow canonical redirects" do
556
+ @query.options.update({:no_redirect => 1})
557
+ request = @query.connection(nil, willSendRequest:@request, redirectResponse:nil)
558
+ request.should.equal @request
559
+ end
560
+
561
+ it "should disallow non-canonical redirects if requested not to" do
562
+ @query.options.update({:no_redirect => 1})
563
+ request = @query.connection(nil, willSendRequest:@request, redirectResponse:"monkey")
564
+ request.should.equal nil
565
+ end
566
+
567
+ it "should allow non-canonical redirects by default" do
568
+ @query.options.delete(:no_redirect)
569
+ request = @query.connection(nil, willSendRequest:@request, redirectResponse:"monkey")
570
+ request.should.equal @request
571
+ end
572
+
573
+ describe "after 30 redirects" do
574
+ before do
575
+ 31.times do
576
+ @query.connection(nil, willSendRequest:@request, redirectResponse:nil)
577
+ end
578
+ end
579
+
580
+ it "sets the error message on response" do
581
+ @real_response.error_message.should.equal "Too many redirections"
582
+ end
583
+
584
+ it "sets the request.done_loading" do
585
+ @query.request.done_loading?.should.equal true
586
+ end
587
+
588
+ it "calls the delegator block" do
589
+ @delegator_was_called.should.equal true
590
+ end
591
+ end
592
+
593
+ it "should update the request URL after redirecting by default" do
594
+ query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, {} )
595
+ redirected_request = NSMutableURLRequest.requestWithURL NSURL.URLWithString('http://expanded.local/')
596
+ query.connection(nil, willSendRequest:redirected_request, redirectResponse:nil)
597
+ query.connectionDidFinishLoading(nil)
598
+ query.response.url.absoluteString.should.equal redirected_request.URL.absoluteString
599
+ query.response.original_url.absoluteString.should.equal @localhost_url
600
+ end
601
+
602
+ end
603
+
604
+ describe "didReceiveAuthenticationChallenge" do
605
+ before do
606
+ @challenge = FakeChallenge.new
607
+ @challenge.previousFailureCount = 0
608
+ @query.connection(nil, didReceiveAuthenticationChallenge:@challenge)
609
+ end
610
+
611
+ it "should cancel the authentication if the failure count was not 0" do
612
+ @challenge.previousFailureCount = 1
613
+ @query.connection(nil, didReceiveAuthenticationChallenge:@challenge)
614
+ @challenge.sender.was_cancelled.should.equal true
615
+ end
616
+
617
+ it "should pass in Credentials and the challenge itself to the sender" do
618
+ @challenge.sender.challenge.should.equal @challenge
619
+ @challenge.sender.credential.user.should.equal @credentials[:username]
620
+ @challenge.sender.credential.password.should.equal @credentials[:password]
621
+ end
622
+
623
+ it "should use Credential Persistence set in options" do
624
+ @challenge.sender.credential.persistence.should.equal @credential_persistence
625
+ end
626
+
627
+ it 'should continue without credentials when no credentials provided' do
628
+ @options.delete :credentials
629
+ query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, @options )
630
+ query.connection(nil, didReceiveAuthenticationChallenge:@challenge)
631
+ @challenge.sender.continue_without_credential.should.equal true
632
+ end
633
+
634
+ end
635
+
636
+ describe "empty payload" do
637
+
638
+ before do
639
+ @payload = {}
640
+ @url_string = 'http://fake.url/method'
641
+ @get_query = BubbleWrap::HTTP::Query.new(@url_string, :get, :payload => @payload)
642
+ end
643
+
644
+ it "should not append a ? to the end of the URL" do
645
+ @get_query.instance_variable_get(:@url).description.should.equal('http://fake.url/method')
646
+ end
647
+
648
+ end
649
+
650
+ describe "properly format payload to url get query string" do
651
+
652
+ before do
653
+ @payload = {"we love" => '#==Rock&Roll==#', "radio" => "Ga Ga", "qual" => 3.0, "incr" => -1, "RFC3986" => "!*'();:@&=+$,/?%#[]"}
654
+ @url_string = 'http://fake.url/method'
655
+ @get_query = BubbleWrap::HTTP::Query.new(@url_string, :get, :payload => @payload)
656
+ @escaped_url = "http://fake.url/method?we%20love=%23%3D%3DRock%26Roll%3D%3D%23&radio=Ga%20Ga&qual=3.0&incr=-1&RFC3986=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D"
657
+ end
658
+
659
+ it "should escape !*'();:@&=+$,/?%#[] characters only in keys and values" do
660
+ @get_query.instance_variable_get(:@url).description.should.equal @escaped_url
661
+ end
662
+
663
+ end
664
+
665
+ describe 'properly support cookie-option for nsmutableurlrequest' do
666
+
667
+ before do
668
+ @no_cookie_query = BubbleWrap::HTTP::Query.new("http://haz-no-cookiez.url", :get, {:payload => {:something => "else"}, :cookies => false})
669
+ @cookie_query = BubbleWrap::HTTP::Query.new("http://haz-cookiez.url", :get, :payload => {:something => "else"})
670
+ end
671
+
672
+ it 'should disabled cookie-usage on nsurlrequest' do
673
+ @no_cookie_query.instance_variable_get(:@request).HTTPShouldHandleCookies.should.equal false
674
+ end
675
+
676
+ it 'should keep sane cookie-related defaults on nsurlrequest' do
677
+ @cookie_query.instance_variable_get(:@request).HTTPShouldHandleCookies.should.equal true
678
+ end
679
+
680
+
681
+ end
682
+
683
+ class FakeSender
684
+ attr_reader :challenge, :credential, :was_cancelled, :continue_without_credential
685
+ def cancelAuthenticationChallenge(challenge)
686
+ @was_cancelled = true
687
+ end
688
+ def useCredential(credential, forAuthenticationChallenge:challenge)
689
+ @challenge = challenge
690
+ @credential = credential
691
+ end
692
+ def continueWithoutCredentialForAuthenticationChallenge(challenge)
693
+ @continue_without_credential = true
694
+ end
695
+ end
696
+
697
+ class FakeChallenge
698
+ attr_accessor :previousFailureCount
699
+
700
+ def sender
701
+ @fake_sender ||= FakeSender.new
702
+ end
703
+ end
704
+
705
+ class BubbleWrap::HTTP::Query
706
+ def create_connection(request, delegate); FakeURLConnection.new(request, delegate); end
707
+ end
708
+
709
+ class FakeURLConnection < NSURLConnection
710
+ attr_reader :delegate, :request, :was_started
711
+ def initialize(request, delegate)
712
+ @request = request
713
+ @delegate = delegate
714
+ self.class.connectionWithRequest(request, delegate:delegate)
715
+ end
716
+ def start
717
+ @was_started = true
718
+ super
719
+ end
720
+ end
721
+
722
+ class FakeURLResponse < NSHTTPURLResponse
723
+ attr_reader :statusCode, :allHeaderFields, :expectedContentLength
724
+ def initialize(status_code, headers, length)
725
+ @statusCode = status_code
726
+ @allHeaderFields = headers
727
+ @expectedContentLength = length
728
+ end
729
+ end
730
+
731
+ end