rspec-cramp 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,19 +3,22 @@ A set of rspec matchers and helpers that make it easier to write specs for [cram
3
3
  Quick start
4
4
  -----------
5
5
 
6
- gem install rspec-cramp
6
+ > gem install rspec-cramp
7
7
 
8
8
 
9
9
 
10
10
  require 'rspec/cramp'
11
11
 
12
+ # Decorate your spec with :cramp => true
12
13
  describe HelloWorld, :cramp => true do
14
+
15
+ # You need to define this method
13
16
  def app
14
- HelloWorld
17
+ HelloWorld # Here goes your cramp application, action or http routes.
15
18
  end
16
19
 
20
+ # Matching on status code.
17
21
  it "should respond to a GET request" do
18
- # Various ways to match a response code.
19
22
  get("/").should respond_with :status => :ok # Matches responses from 200 to 299.
20
23
  get("/").should respond_with :status => 200 # Matches only 200.
21
24
  get("/").should respond_with :status => "200" # Same as above.
@@ -23,19 +26,45 @@ Quick start
23
26
  get("/").should_not respond_with :status => :error # Matches any HTTP error.
24
27
  end
25
28
 
29
+ # Matching on response body.
26
30
  it "should respond with text starting with 'Hello'" do
27
31
  get("/").should respond_with :body => /^Hello.*/
28
32
  end
29
-
30
33
  it "should respond with 'Hello, world!'" do
31
34
  get("/").should respond_with :body => "Hello, world!"
32
35
  end
33
-
36
+
37
+ # Matching on response headers.
34
38
  it "should respond with html" do
35
39
  get("/").should respond_with :headers => {"Content-Type" => "text/html"}
36
40
  get("/").should_not respond_with :headers => {"Content-Type" => "text/plain"}
37
41
  get("/").should_not respond_with :headers => {"Unexpected-Header" => /.*/}
38
42
  end
43
+
44
+ # Matching using lambdas.
45
+ it "should match my sophisticated custom matchers" do
46
+ # Entire headers.
47
+ status_check = lambda {|status| status.between?(200, 299)}
48
+ body_check = lambda {|body| body =~ /.*el.*/}
49
+ headers_check = lambda {|headers| true} # Any headers will do.
50
+ get("/").should respond_with :status => status_check, :body => body_check, :headers => headers_check
51
+ # Header value.
52
+ get("/").should respond_with :headers => {"Content-Type" => lambda {|value| value == "text/html"}}
53
+ get("/").should_not respond_with :headers => {"Content-Type" => lambda {|value| value == "text/plain"}}
54
+ end
55
+
56
+ # Supports POST/GET/PUT/DELETE and you don't have to use the matcher.
57
+ it "should work without a matcher" do
58
+ get "/"
59
+ post "/"
60
+ put "/"
61
+ delete "/"
62
+ end
63
+
64
+ # Request params & custom headers.
65
+ it "should accept my params" do
66
+ post("/", :params => {:text => "whatever"}, :headers => {"Custom-Header" => "blah"})
67
+ end
39
68
  end
40
69
 
41
70
  The matcher is fairly flexible, supports regular expressions and also works with multipart responses (more than one `Cramp::Action.render`), SSE and so on. I'll create more examples and docs but for the time being, pls. take a look at the code and [examples](https://github.com/bilus/rspec-cramp/tree/master/spec/examples).
@@ -47,11 +76,15 @@ Project status
47
76
 
48
77
  **IMPORTANT:** This is work in progress.
49
78
 
50
- 1. There are still some things I'll take care of soon (esp. better failure messages).
51
- 2. I extracted the code from one of my projects and rewrote the matchers from scratch test-first. Still, after the weekend I plan to actually use it to replace the 'legacy' matchers in my project; this will probably uncover some bugs and may make me add more functionality. *UPDATE: I'm working on it right now.*
79
+ There are still some things I'll take care of soon (esp. better failure messages).
52
80
 
53
81
  If you have any comments regarding the code as it is now (I know it's a bit messy), please feel free to tweet [@MartinBilski](http://twitter.com/#!/MartinBilski)
54
82
 
83
+ Contributors
84
+ ------------
85
+
86
+ [Ivan Fomichev](https://github.com/codeholic)
87
+
55
88
  Notes
56
89
  ----
57
90
 
@@ -7,6 +7,8 @@ RSpec::Matchers.define :respond_with do |options = {}|
7
7
  response.matching?(options)
8
8
  end
9
9
 
10
+ # TODO Better message for cases such as: specify { get("/").should respond_with :status => :ok }.
11
+
10
12
  failure_message_for_should do
11
13
  @actual_response.last_failure_message_for_should
12
14
  end
@@ -14,8 +14,7 @@ module RSpec
14
14
  stopping = false
15
15
  deferred_body = @body
16
16
  chunks = []
17
- deferred_body.each do |chunk|
18
- chunks << chunk unless stopping
17
+ check = lambda do
19
18
  if chunks.count >= max_chunks
20
19
  @body = chunks
21
20
  stopping = true
@@ -23,6 +22,11 @@ module RSpec
23
22
  EM.next_tick { EM.stop }
24
23
  end
25
24
  end
25
+ check.call
26
+ deferred_body.each do |chunk|
27
+ chunks << chunk unless stopping
28
+ check.call
29
+ end
26
30
  end
27
31
  end
28
32
 
@@ -117,8 +121,13 @@ module RSpec
117
121
  end
118
122
 
119
123
  def matching_headers?(expected_header)
120
- expected_header.nil? ||
121
- (matching_header_keys?(expected_header) && matching_header_values?(expected_header))
124
+ if expected_header.nil?
125
+ true
126
+ elsif expected_header.is_a? Proc
127
+ matching_response_element?(:headers, @headers, expected_header)
128
+ else
129
+ matching_header_keys?(expected_header) && matching_header_values?(expected_header)
130
+ end
122
131
  end
123
132
 
124
133
  def matching_body?(expected_body)
@@ -1,13 +1,16 @@
1
1
  require File.join(File.dirname(__FILE__), "../spec_helper")
2
2
  require File.join(File.dirname(__FILE__), "sample_actions")
3
3
 
4
+ # Decorate your spec with :cramp => true
4
5
  describe HelloWorld, :cramp => true do
6
+
7
+ # You need to define this method
5
8
  def app
6
- HelloWorld
9
+ HelloWorld # Here goes your cramp application, action or http routes.
7
10
  end
8
11
 
12
+ # Matching on status code.
9
13
  it "should respond to a GET request" do
10
- # Variaus ways to match a response code.
11
14
  get("/").should respond_with :status => :ok # Matches responses from 200 to 299.
12
15
  get("/").should respond_with :status => 200 # Matches only 200.
13
16
  get("/").should respond_with :status => "200" # Same as above.
@@ -15,17 +18,43 @@ describe HelloWorld, :cramp => true do
15
18
  get("/").should_not respond_with :status => :error # Matches any HTTP error.
16
19
  end
17
20
 
21
+ # Matching on response body.
18
22
  it "should respond with text starting with 'Hello'" do
19
23
  get("/").should respond_with :body => /^Hello.*/
20
24
  end
21
-
22
25
  it "should respond with 'Hello, world!'" do
23
26
  get("/").should respond_with :body => "Hello, world!"
24
27
  end
25
28
 
29
+ # Matching on response headers.
26
30
  it "should respond with html" do
27
31
  get("/").should respond_with :headers => {"Content-Type" => "text/html"}
28
32
  get("/").should_not respond_with :headers => {"Content-Type" => "text/plain"}
29
33
  get("/").should_not respond_with :headers => {"Unexpected-Header" => /.*/}
30
34
  end
35
+
36
+ # Matching using lambdas.
37
+ it "should match my sophisticated custom matchers" do
38
+ # Entire headers.
39
+ status_check = lambda {|status| status.between?(200, 299)}
40
+ body_check = lambda {|body| body =~ /.*el.*/}
41
+ headers_check = lambda {|headers| true} # Any headers will do.
42
+ get("/").should respond_with :status => status_check, :body => body_check, :headers => headers_check
43
+ # Header value.
44
+ get("/").should respond_with :headers => {"Content-Type" => lambda {|value| value == "text/html"}}
45
+ get("/").should_not respond_with :headers => {"Content-Type" => lambda {|value| value == "text/plain"}}
46
+ end
47
+
48
+ # Supports POST/GET/PUT/DELETE and you don't have to use the matcher.
49
+ it "should work without a matcher" do
50
+ get "/"
51
+ post "/"
52
+ put "/"
53
+ delete "/"
54
+ end
55
+
56
+ # Request params & custom headers.
57
+ it "should accept my params" do
58
+ post("/", :params => {:text => "whatever"}, :headers => {"Custom-Header" => "blah"})
59
+ end
31
60
  end
@@ -91,6 +91,10 @@ module Cramp
91
91
  end
92
92
  end
93
93
 
94
+ class SseNoRenderAction < Cramp::Action
95
+ self.transport = :sse
96
+ end
97
+
94
98
  class RequestHeaders < Cramp::Action
95
99
  on_start :render_request_headers
96
100
  def render_request_headers
@@ -127,10 +131,11 @@ module Cramp
127
131
  add('/raise_on_start').to RaiseOnStart
128
132
  add('/raise_on_finish').to RaiseOnFinish
129
133
  add('/sse').to SseAction
130
- add('/get_only').request_method('GET').to SuccessfulResponse
131
- add('/post_only').request_method('POST').to SuccessfulResponse
132
- add('/put_only').request_method('PUT').to SuccessfulResponse
133
- add('/delete_only').request_method('DELETE').to SuccessfulResponse
134
+ add('/sse_no_render').to SseNoRenderAction
135
+ get('/get_only').to SuccessfulResponse
136
+ post('/post_only').to SuccessfulResponse
137
+ put('/put_only').to SuccessfulResponse
138
+ delete('/delete_only').to SuccessfulResponse
134
139
  add('/request_headers').to RequestHeaders
135
140
  add('/request_params').to RequestParams
136
141
  end
@@ -160,7 +165,6 @@ module Cramp
160
165
  end
161
166
  end
162
167
 
163
- # TODO Rewrite the repetitive code below using data-based spec generation.
164
168
  describe "exact match on response status" do
165
169
  it "should match successful response" do
166
170
  send(method, "/200").should respond_with :status => 200
@@ -255,6 +259,21 @@ module Cramp
255
259
  it "should not match iff the header isn't there" do
256
260
  send(method, "/custom_header").should_not respond_with :headers => {/Non\-Existent\-One/i => /^QWERTY$/}
257
261
  end
262
+ end
263
+
264
+ describe "lambda match on entire header " do
265
+ it "should match when true" do
266
+ match_headers = lambda do |headers|
267
+ headers.find {|(k, v)| k == "Extra-Header" && v == "ABCD"}
268
+ end
269
+ send(method, "/custom_header").should respond_with :headers => match_headers
270
+ end
271
+ it "should not match when false" do
272
+ match_headers = lambda do |headers|
273
+ headers.find {|(k, v)| k == "Non-Existent-One" && v == "QWERTY"}
274
+ end
275
+ send(method, "/custom_header").should_not respond_with :headers => match_headers
276
+ end
258
277
  end
259
278
 
260
279
  # FIXME How to handle a situation where nothing is rendered? get reads the body...
@@ -345,6 +364,11 @@ module Cramp
345
364
  send(method, "/sse", :max_chunks => 2).should respond_with :chunks => [/^data: Hello 1.*/, /^data: Hello 2.*/]
346
365
  send(method, "/sse", :max_chunks => 2).should_not respond_with :chunks => [/.*Incorrect 1.*/, /.*Incorrect 2.*/]
347
366
  end
367
+
368
+ it "should not wait for body if it is skipped explicitly" do
369
+ lambda { send(method, "/sse_no_render", :max_chunks => 0) }.should_not raise_error Timeout::Error
370
+ send(method, "/sse_no_render", :max_chunks => 0).should respond_with :status => 200, :body => ""
371
+ end
348
372
  end
349
373
 
350
374
  it "should correctly handle exception in the callbacks" do
@@ -385,7 +409,6 @@ module Cramp
385
409
  end
386
410
  end
387
411
 
388
- # TODO Add method-specific paths to http routes and write specs.
389
412
  describe "GET request" do
390
413
  it_should_behave_like "async_request", :get
391
414
 
@@ -437,9 +460,8 @@ module Cramp
437
460
  routes
438
461
  end
439
462
 
440
- # TODO Only basic matching here because respond_with matcher uses the same method.
441
- # It should be the other way around, i.e. all the tests should be here but I hate to rewrite the specs now.
442
- # Anyway, we need more tests here.
463
+ # Note: Only basic specs here because respond_with matcher uses the same code and is extensively tested.
464
+
443
465
  it "should support expectations on response status" do
444
466
  get("/200") do |response|
445
467
  response.should be_matching :status => 200
@@ -488,4 +510,4 @@ module Cramp
488
510
  end
489
511
  end
490
512
  end
491
- end
513
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require "rubygems"
2
- require "cramp"
3
- require "rspec"
2
+ require "bundler/setup"
3
+
4
4
  require "http_router"
5
5
 
6
6
  $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))