bubble-wrap 1.2.0 → 1.3.0.osx

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