rspec-rails 2.99.0 → 3.1.0

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 (80) hide show
  1. checksums.yaml +6 -14
  2. checksums.yaml.gz.sig +1 -0
  3. data/.yardopts +1 -1
  4. data/Capybara.md +1 -3
  5. data/Changelog.md +148 -11
  6. data/License.txt +1 -0
  7. data/README.md +202 -105
  8. data/lib/generators/rspec/controller/controller_generator.rb +1 -0
  9. data/lib/generators/rspec/controller/templates/controller_spec.rb +5 -5
  10. data/lib/generators/rspec/controller/templates/view_spec.rb +2 -2
  11. data/lib/generators/rspec/feature/feature_generator.rb +16 -0
  12. data/lib/generators/rspec/feature/templates/feature_spec.rb +5 -0
  13. data/lib/generators/rspec/helper/helper_generator.rb +1 -0
  14. data/lib/generators/rspec/helper/templates/helper_spec.rb +2 -2
  15. data/lib/generators/rspec/install/install_generator.rb +44 -5
  16. data/lib/generators/rspec/install/templates/spec/{spec_helper.rb.tt → rails_helper.rb} +19 -28
  17. data/lib/generators/rspec/integration/integration_generator.rb +1 -12
  18. data/lib/generators/rspec/integration/templates/request_spec.rb +3 -8
  19. data/lib/generators/rspec/job/job_generator.rb +12 -0
  20. data/lib/generators/rspec/job/templates/job_spec.rb.erb +7 -0
  21. data/lib/generators/rspec/mailer/mailer_generator.rb +1 -0
  22. data/lib/generators/rspec/mailer/templates/mailer_spec.rb +6 -6
  23. data/lib/generators/rspec/model/model_generator.rb +19 -5
  24. data/lib/generators/rspec/model/templates/fixtures.yml +1 -1
  25. data/lib/generators/rspec/model/templates/model_spec.rb +2 -2
  26. data/lib/generators/rspec/observer/observer_generator.rb +1 -0
  27. data/lib/generators/rspec/observer/templates/observer_spec.rb +2 -2
  28. data/lib/generators/rspec/scaffold/scaffold_generator.rb +108 -135
  29. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +34 -39
  30. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +5 -13
  31. data/lib/generators/rspec/scaffold/templates/index_spec.rb +3 -10
  32. data/lib/generators/rspec/scaffold/templates/new_spec.rb +6 -14
  33. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +9 -9
  34. data/lib/generators/rspec/scaffold/templates/show_spec.rb +4 -11
  35. data/lib/generators/rspec/view/templates/view_spec.rb +2 -2
  36. data/lib/generators/rspec/view/view_generator.rb +1 -0
  37. data/lib/generators/rspec.rb +16 -6
  38. data/lib/rspec/rails/adapters.rb +33 -35
  39. data/lib/rspec/rails/configuration.rb +97 -0
  40. data/lib/rspec/rails/example/controller_example_group.rb +173 -159
  41. data/lib/rspec/rails/example/feature_example_group.rb +25 -20
  42. data/lib/rspec/rails/example/helper_example_group.rb +27 -26
  43. data/lib/rspec/rails/example/mailer_example_group.rb +29 -14
  44. data/lib/rspec/rails/example/model_example_group.rb +7 -7
  45. data/lib/rspec/rails/example/rails_example_group.rb +1 -0
  46. data/lib/rspec/rails/example/request_example_group.rb +19 -17
  47. data/lib/rspec/rails/example/routing_example_group.rb +40 -39
  48. data/lib/rspec/rails/example/view_example_group.rb +140 -137
  49. data/lib/rspec/rails/example.rb +0 -33
  50. data/lib/rspec/rails/extensions/active_record/proxy.rb +0 -1
  51. data/lib/rspec/rails/extensions.rb +0 -1
  52. data/lib/rspec/rails/feature_check.rb +35 -0
  53. data/lib/rspec/rails/fixture_support.rb +5 -12
  54. data/lib/rspec/rails/matchers/be_a_new.rb +67 -62
  55. data/lib/rspec/rails/matchers/be_new_record.rb +23 -21
  56. data/lib/rspec/rails/matchers/be_valid.rb +41 -33
  57. data/lib/rspec/rails/matchers/have_http_status.rb +361 -0
  58. data/lib/rspec/rails/matchers/have_rendered.rb +35 -31
  59. data/lib/rspec/rails/matchers/redirect_to.rb +30 -29
  60. data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
  61. data/lib/rspec/rails/matchers/routing_matchers.rb +104 -94
  62. data/lib/rspec/rails/matchers.rb +7 -4
  63. data/lib/rspec/rails/tasks/rspec.rake +1 -1
  64. data/lib/rspec/rails/vendor/capybara.rb +2 -33
  65. data/lib/rspec/rails/version.rb +3 -1
  66. data/lib/rspec/rails/view_assigns.rb +18 -18
  67. data/lib/rspec/rails/view_rendering.rb +20 -45
  68. data/lib/rspec/rails.rb +2 -13
  69. data/lib/rspec-rails.rb +4 -1
  70. data.tar.gz.sig +0 -0
  71. metadata +80 -99
  72. metadata.gz.sig +3 -0
  73. data/lib/autotest/rails_rspec2.rb +0 -91
  74. data/lib/generators/rspec/install/templates/.rspec +0 -1
  75. data/lib/rspec/rails/extensions/active_record/base.rb +0 -58
  76. data/lib/rspec/rails/infer_type_configuration.rb +0 -26
  77. data/lib/rspec/rails/matchers/have_extension.rb +0 -36
  78. data/lib/rspec/rails/mocks.rb +0 -284
  79. data/lib/rspec/rails/module_inclusion.rb +0 -19
  80. data/lib/rspec/rails/vendor/webrat.rb +0 -33
@@ -1,76 +1,81 @@
1
- module RSpec::Rails::Matchers
2
- class BeANew < RSpec::Matchers::BuiltIn::BaseMatcher
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # @api private
5
+ #
6
+ # Matcher class for `be_a_new`. Should not be instantiated directly.
7
+ #
8
+ # @see RSpec::Rails::Matchers#be_a_new
9
+ class BeANew < RSpec::Matchers::BuiltIn::BaseMatcher
10
+ # @private
11
+ def initialize(expected)
12
+ @expected = expected
13
+ end
3
14
 
4
- def initialize(expected)
5
- @expected = expected
6
- end
15
+ # @private
16
+ def matches?(actual)
17
+ @actual = actual
18
+ actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
19
+ end
7
20
 
8
- # @api private
9
- def matches?(actual)
10
- @actual = actual
11
- actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
12
- end
21
+ # @api public
22
+ # @see RSpec::Rails::Matchers#be_a_new
23
+ def with(expected_attributes)
24
+ attributes.merge!(expected_attributes)
25
+ self
26
+ end
13
27
 
14
- # Use this to specify the specific attributes to match on the new record.
15
- #
16
- # @example
17
- #
18
- # it "assigns a new Thing with the submitted attributes" do
19
- # post :create, :thing => { :name => "Illegal Value" }
20
- # assigns(:thing).should be_a_new(Thing).with(:name => nil)
21
- # end
22
- def with(expected_attributes)
23
- attributes.merge!(expected_attributes)
24
- self
25
- end
28
+ # @private
29
+ def failure_message
30
+ [].tap do |message|
31
+ unless actual.is_a?(expected) && actual.new_record?
32
+ message << "expected #{actual.inspect} to be a new #{expected.inspect}"
33
+ end
34
+ unless attributes_match?(actual)
35
+ if unmatched_attributes.size > 1
36
+ message << "attributes #{unmatched_attributes.inspect} were not set on #{actual.inspect}"
37
+ else
38
+ message << "attribute #{unmatched_attributes.inspect} was not set on #{actual.inspect}"
39
+ end
40
+ end
41
+ end.join(' and ')
42
+ end
26
43
 
27
- # @api private
28
- def failure_message_for_should
29
- [].tap do |message|
30
- unless actual.is_a?(expected) && actual.new_record?
31
- message << "expected #{actual.inspect} to be a new #{expected.inspect}"
44
+ private
45
+
46
+ def attributes
47
+ @attributes ||= {}
32
48
  end
33
- unless attributes_match?(actual)
34
- if unmatched_attributes.size > 1
35
- message << "attributes #{unmatched_attributes.inspect} were not set on #{actual.inspect}"
36
- else
37
- message << "attribute #{unmatched_attributes.inspect} was not set on #{actual.inspect}"
49
+
50
+ def attributes_match?(actual)
51
+ attributes.stringify_keys.all? do |key, value|
52
+ actual.attributes[key].eql?(value)
38
53
  end
39
54
  end
40
- end.join(' and ')
41
- end
42
-
43
- private
44
55
 
45
- def attributes
46
- @attributes ||= {}
47
- end
48
-
49
- def attributes_match?(actual)
50
- attributes.stringify_keys.all? do |key, value|
51
- actual.attributes[key].eql?(value)
56
+ def unmatched_attributes
57
+ attributes.stringify_keys.reject do |key, value|
58
+ actual.attributes[key].eql?(value)
59
+ end
60
+ end
52
61
  end
53
- end
54
62
 
55
- def unmatched_attributes
56
- attributes.stringify_keys.reject do |key, value|
57
- actual.attributes[key].eql?(value)
63
+ # Passes if actual is an instance of `model_class` and returns `false` for
64
+ # `persisted?`. Typically used to specify instance variables assigned to
65
+ # views by controller actions
66
+ #
67
+ # Use the `with` method to specify the specific attributes to match on the
68
+ # new record.
69
+ #
70
+ # @example
71
+ # get :new
72
+ # assigns(:thing).should be_a_new(Thing)
73
+ #
74
+ # post :create, :thing => { :name => "Illegal Value" }
75
+ # assigns(:thing).should be_a_new(Thing).with(:name => nil)
76
+ def be_a_new(model_class)
77
+ BeANew.new(model_class)
58
78
  end
59
79
  end
60
80
  end
61
-
62
- # Passes if actual is an instance of `model_class` and returns `false` for
63
- # `persisted?`. Typically used to specify instance variables assigned to
64
- # views by controller actions
65
- #
66
- # @example
67
- #
68
- # get :new
69
- # assigns(:thing).should be_a_new(Thing)
70
- #
71
- # post :create, :thing => { :name => "Illegal Value" }
72
- # assigns(:thing).should be_a_new(Thing).with(:name => nil)
73
- def be_a_new(model_class)
74
- BeANew.new(model_class)
75
- end
76
81
  end
@@ -1,27 +1,29 @@
1
- module RSpec::Rails::Matchers
2
- class BeANewRecord < RSpec::Matchers::BuiltIn::BaseMatcher
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # @private
5
+ class BeANewRecord < RSpec::Matchers::BuiltIn::BaseMatcher
6
+ def matches?(actual)
7
+ !actual.persisted?
8
+ end
3
9
 
4
- # @api private
5
- def matches?(actual)
6
- !actual.persisted?
7
- end
10
+ def failure_message
11
+ "expected #{actual.inspect} to be a new record, but was persisted"
12
+ end
8
13
 
9
- def failure_message_for_should
10
- "expected #{actual.inspect} to be a new record, but was persisted"
11
- end
14
+ def failure_message_when_negated
15
+ "expected #{actual.inspect} to be persisted, but was a new record"
16
+ end
17
+ end
12
18
 
13
- def failure_message_for_should_not
14
- "expected #{actual.inspect} to be persisted, but was a new record"
19
+ # Passes if actual returns `false` for `persisted?`.
20
+ #
21
+ # @example
22
+ # get :new
23
+ # expect(assigns(:thing)).to be_new_record
24
+ def be_new_record
25
+ BeANewRecord.new
26
+ end
15
27
  end
16
28
  end
17
-
18
- # Passes if actual returns `false` for `persisted?`.
19
- #
20
- # @example
21
- #
22
- # get :new
23
- # assigns(:thing).should be_new_record
24
- def be_new_record
25
- BeANewRecord.new
26
- end
27
29
  end
@@ -1,40 +1,48 @@
1
- module RSpec::Rails::Matchers
2
- class BeValid < RSpec::Matchers::BuiltIn::Be
3
- def initialize(*args)
4
- @args = args
5
- end
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # @private
5
+ class BeValid < RSpec::Matchers::BuiltIn::Be
6
+ def initialize(*args)
7
+ @args = args
8
+ end
6
9
 
7
- # @api private
8
- def matches?(actual)
9
- @actual = actual
10
- actual.valid?(*@args)
11
- end
10
+ def matches?(actual)
11
+ @actual = actual
12
+ actual.valid?(*@args)
13
+ end
12
14
 
13
- # @api private
14
- def failure_message_for_should
15
- message = "expected #{actual.inspect} to be valid"
16
- if actual.respond_to?(:errors)
17
- message << ", but got errors: #{actual.errors.full_messages.join(', ')}"
18
- end
15
+ def failure_message
16
+ message = "expected #{actual.inspect} to be valid"
19
17
 
20
- message
21
- end
18
+ if actual.respond_to?(:errors)
19
+ errors = if actual.errors.respond_to?(:full_messages)
20
+ actual.errors.full_messages
21
+ else
22
+ actual.errors
23
+ end
22
24
 
23
- # @api private
24
- def failure_message_for_should_not
25
- "expected #{actual.inspect} not to be valid"
26
- end
27
- end
25
+ message << ", but got errors: #{errors.map(&:to_s).join(', ')}"
26
+ end
28
27
 
29
- # Passes if the given model instance's `valid?` method is true, meaning all
30
- # of the `ActiveModel::Validations` passed and no errors exist. If a message
31
- # is not given, a default message is shown listing each error.
32
- #
33
- # @example
34
- #
35
- # thing = Thing.new
36
- # thing.should be_valid
37
- def be_valid(*args)
38
- BeValid.new(*args)
28
+ message
29
+ end
30
+
31
+ def failure_message_when_negated
32
+ "expected #{actual.inspect} not to be valid"
33
+ end
34
+ end
35
+
36
+ # Passes if the given model instance's `valid?` method is true, meaning
37
+ # all of the `ActiveModel::Validations` passed and no errors exist. If a
38
+ # message is not given, a default message is shown listing each error.
39
+ #
40
+ # @example
41
+ # thing = Thing.new
42
+ # expect(thing).to be_valid
43
+ def be_valid(*args)
44
+ BeValid.new(*args)
45
+ end
46
+ end
39
47
  end
40
48
  end
@@ -0,0 +1,361 @@
1
+ # The following code inspired and modified from Rails' `assert_response`:
2
+ #
3
+ # https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/assertions/response.rb#L22-L38
4
+ #
5
+ # Thank you to all the Rails devs who did the heavy lifting on this!
6
+
7
+ module RSpec
8
+ module Rails
9
+ module Matchers
10
+ # Namespace for various implementations of `have_http_status`.
11
+ #
12
+ # @api private
13
+ module HaveHttpStatus
14
+ # Instantiates an instance of the proper matcher based on the provided
15
+ # `target`.
16
+ #
17
+ # @param target [Object] expected http status or code
18
+ # @return response matcher instance
19
+ def self.matcher_for_status(target)
20
+ if GenericStatus.valid_statuses.include?(target)
21
+ GenericStatus.new(target)
22
+ elsif Symbol === target
23
+ SymbolicStatus.new(target)
24
+ else
25
+ NumericCode.new(target)
26
+ end
27
+ end
28
+
29
+ # @api private
30
+ # Conversion function to coerce the provided object into an
31
+ # `ActionDispatch::TestResponse`.
32
+ #
33
+ # @param obj [Object] object to convert to a response
34
+ # @return [ActionDispatch::TestResponse]
35
+ def as_test_response(obj)
36
+ if ::ActionDispatch::Response === obj
37
+ ::ActionDispatch::TestResponse.from_response(obj)
38
+ elsif ::ActionDispatch::TestResponse === obj
39
+ obj
40
+ elsif obj.respond_to?(:status_code) && obj.respond_to?(:response_headers)
41
+ # Acts As Capybara Session
42
+ # Hack to support `Capybara::Session` without having to load
43
+ # Capybara or catch `NameError`s for the undefined constants
44
+ ::ActionDispatch::TestResponse.new.tap do |resp|
45
+ resp.status = obj.status_code
46
+ resp.headers = obj.response_headers
47
+ resp.body = obj.body
48
+ end
49
+ else
50
+ raise TypeError, "Invalid response type: #{obj}"
51
+ end
52
+ end
53
+ module_function :as_test_response
54
+
55
+ # @return [String, nil] a formatted failure message if
56
+ # `@invalid_response` is present, `nil` otherwise
57
+ def invalid_response_type_message
58
+ return unless @invalid_response
59
+ "expected a response object, but an instance of " \
60
+ "#{@invalid_response.class} was received"
61
+ end
62
+
63
+ # @api private
64
+ # Provides an implementation for `have_http_status` matching against
65
+ # numeric http status codes.
66
+ #
67
+ # Not intended to be instantiated directly.
68
+ #
69
+ # @example
70
+ # expect(response).to have_http_status(404)
71
+ #
72
+ # @see RSpec::Rails::Matchers.have_http_status
73
+ class NumericCode < RSpec::Matchers::BuiltIn::BaseMatcher
74
+ include HaveHttpStatus
75
+
76
+ def initialize(code)
77
+ @expected = code.to_i
78
+ @actual = nil
79
+ @invalid_response = nil
80
+ end
81
+
82
+ # @param [Object] response object providing an http code to match
83
+ # @return [Boolean] `true` if the numeric code matched the `response` code
84
+ def matches?(response)
85
+ test_response = as_test_response(response)
86
+ @actual = test_response.response_code
87
+ expected == @actual
88
+ rescue TypeError => _ignored
89
+ @invalid_response = response
90
+ false
91
+ end
92
+
93
+ # @return [String]
94
+ def description
95
+ "respond with numeric status code #{expected}"
96
+ end
97
+
98
+ # @return [String] explaining why the match failed
99
+ def failure_message
100
+ invalid_response_type_message ||
101
+ "expected the response to have status code #{expected.inspect}" \
102
+ " but it was #{actual.inspect}"
103
+ end
104
+
105
+ # @return [String] explaining why the match failed
106
+ def failure_message_when_negated
107
+ invalid_response_type_message ||
108
+ "expected the response not to have status code " \
109
+ "#{expected.inspect} but it did"
110
+ end
111
+ end
112
+
113
+ # @api private
114
+ # Provides an implementation for `have_http_status` matching against
115
+ # Rack symbol http status codes.
116
+ #
117
+ # Not intended to be instantiated directly.
118
+ #
119
+ # @example
120
+ # expect(response).to have_http_status(:created)
121
+ #
122
+ # @see RSpec::Rails::Matchers.have_http_status
123
+ # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
124
+ class SymbolicStatus < RSpec::Matchers::BuiltIn::BaseMatcher
125
+ include HaveHttpStatus
126
+
127
+ def initialize(status)
128
+ @expected_status = status
129
+ @actual = nil
130
+ @invalid_response = nil
131
+ set_expected_code!
132
+ end
133
+
134
+ # @param [Object] response object providing an http code to match
135
+ # @return [Boolean] `true` if Rack's associated numeric HTTP code matched
136
+ # the `response` code
137
+ def matches?(response)
138
+ test_response = as_test_response(response)
139
+ @actual = test_response.response_code
140
+ expected == @actual
141
+ rescue TypeError => _ignored
142
+ @invalid_response = response
143
+ false
144
+ end
145
+
146
+ # @return [String]
147
+ def description
148
+ "respond with status code #{pp_expected}"
149
+ end
150
+
151
+ # @return [String] explaining why the match failed
152
+ def failure_message
153
+ invalid_response_type_message ||
154
+ "expected the response to have status code #{pp_expected} but it" \
155
+ " was #{pp_actual}"
156
+ end
157
+
158
+ # @return [String] explaining why the match failed
159
+ def failure_message_when_negated
160
+ invalid_response_type_message ||
161
+ "expected the response not to have status code #{pp_expected} " \
162
+ "but it did"
163
+ end
164
+
165
+ # The initialized expected status symbol
166
+ attr_reader :expected_status
167
+ private :expected_status
168
+
169
+ private
170
+
171
+ # @return [Symbol] representing the actual http numeric code
172
+ def actual_status
173
+ return unless actual
174
+ @actual_status ||= compute_status_from(actual)
175
+ end
176
+
177
+ # Reverse lookup of the Rack status code symbol based on the numeric
178
+ # http code
179
+ #
180
+ # @param code [Fixnum] http status code to look up
181
+ # @return [Symbol] representing the http numeric code
182
+ def compute_status_from(code)
183
+ status, _ = Rack::Utils::SYMBOL_TO_STATUS_CODE.find do |_, c|
184
+ c == code
185
+ end
186
+ status
187
+ end
188
+
189
+ # @return [String] pretty format the actual response status
190
+ def pp_actual
191
+ pp_status(actual_status, actual)
192
+ end
193
+
194
+ # @return [String] pretty format the expected status and associated code
195
+ def pp_expected
196
+ pp_status(expected_status, expected)
197
+ end
198
+
199
+ # @return [String] pretty format the actual response status
200
+ def pp_status(status, code)
201
+ if status
202
+ "#{status.inspect} (#{code})"
203
+ else
204
+ code.to_s
205
+ end
206
+ end
207
+
208
+ # Sets `expected` to the numeric http code based on the Rack
209
+ # `expected_status` status
210
+ #
211
+ # @see Rack::Utils::SYMBOL_TO_STATUS_CODE
212
+ # @raise [ArgumentError] if an associated code could not be found
213
+ def set_expected_code!
214
+ @expected ||=
215
+ Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(expected_status) do
216
+ raise ArgumentError,
217
+ "Invalid HTTP status: #{expected_status.inspect}"
218
+ end
219
+ end
220
+ end
221
+
222
+ # @api private
223
+ # Provides an implementation for `have_http_status` matching against
224
+ # `ActionDispatch::TestResponse` http status category queries.
225
+ #
226
+ # Not intended to be instantiated directly.
227
+ #
228
+ # @example
229
+ # expect(response).to have_http_status(:success)
230
+ # expect(response).to have_http_status(:error)
231
+ # expect(response).to have_http_status(:missing)
232
+ # expect(response).to have_http_status(:redirect)
233
+ #
234
+ # @see RSpec::Rails::Matchers.have_http_status
235
+ # @see ActionDispatch::TestResponse
236
+ class GenericStatus < RSpec::Matchers::BuiltIn::BaseMatcher
237
+ include HaveHttpStatus
238
+
239
+ # @return [Array<Symbol>] of status codes which represent a HTTP status
240
+ # code "group"
241
+ # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
242
+ def self.valid_statuses
243
+ [:error, :success, :missing, :redirect]
244
+ end
245
+
246
+ def initialize(type)
247
+ unless self.class.valid_statuses.include?(type)
248
+ raise ArgumentError, "Invalid generic HTTP status: #{type.inspect}"
249
+ end
250
+ @expected = type
251
+ @actual = nil
252
+ @invalid_response = nil
253
+ end
254
+
255
+ # @return [Boolean] `true` if Rack's associated numeric HTTP code matched
256
+ # the `response` code
257
+ def matches?(response)
258
+ test_response = as_test_response(response)
259
+ @actual = test_response.response_code
260
+ test_response.send("#{expected}?")
261
+ rescue TypeError => _ignored
262
+ @invalid_response = response
263
+ false
264
+ end
265
+
266
+ # @return [String]
267
+ def description
268
+ "respond with #{type_message}"
269
+ end
270
+
271
+ # @return [String] explaining why the match failed
272
+ def failure_message
273
+ invalid_response_type_message ||
274
+ "expected the response to have #{type_message} but it was #{actual}"
275
+ end
276
+
277
+ # @return [String] explaining why the match failed
278
+ def failure_message_when_negated
279
+ invalid_response_type_message ||
280
+ "expected the response not to have #{type_message} but it was #{actual}"
281
+ end
282
+
283
+ private
284
+
285
+ # @return [String] formating the expected status and associated code(s)
286
+ def type_message
287
+ @type_message ||= (expected == :error ? "an error" : "a #{expected}") +
288
+ " status code (#{type_codes})"
289
+ end
290
+
291
+ # @return [String] formatting the associated code(s) for the various
292
+ # status code "groups"
293
+ # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
294
+ # @see https://github.com/rack/rack/blob/master/lib/rack/response.rb `Rack::Response`
295
+ def type_codes
296
+ # At the time of this commit the most recent version of
297
+ # `ActionDispatch::TestResponse` defines the following aliases:
298
+ #
299
+ # alias_method :success?, :successful?
300
+ # alias_method :missing?, :not_found?
301
+ # alias_method :redirect?, :redirection?
302
+ # alias_method :error?, :server_error?
303
+ #
304
+ # It's parent `ActionDispatch::Response` includes
305
+ # `Rack::Response::Helpers` which defines the aliased methods as:
306
+ #
307
+ # def successful?; status >= 200 && status < 300; end
308
+ # def redirection?; status >= 300 && status < 400; end
309
+ # def server_error?; status >= 500 && status < 600; end
310
+ # def not_found?; status == 404; end
311
+ #
312
+ # @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/testing/test_response.rb#L17-L27
313
+ # @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/http/response.rb#L74
314
+ # @see https://github.com/rack/rack/blob/ce4a3959/lib/rack/response.rb#L119-L122
315
+ @type_codes ||= case expected
316
+ when :error
317
+ "5xx"
318
+ when :success
319
+ "2xx"
320
+ when :missing
321
+ "404"
322
+ when :redirect
323
+ "3xx"
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ # @api public
330
+ # Passes if `response` has a matching HTTP status code.
331
+ #
332
+ # The following symbolic status codes are allowed:
333
+ #
334
+ # - `Rack::Utils::SYMBOL_TO_STATUS_CODE`
335
+ # - One of the defined `ActionDispatch::TestResponse` aliases:
336
+ # - `:error`
337
+ # - `:missing`
338
+ # - `:redirect`
339
+ # - `:success`
340
+ #
341
+ # @example Accepts numeric and symbol statuses
342
+ # expect(response).to have_http_status(404)
343
+ # expect(response).to have_http_status(:created)
344
+ # expect(response).to have_http_status(:success)
345
+ # expect(response).to have_http_status(:error)
346
+ # expect(response).to have_http_status(:missing)
347
+ # expect(response).to have_http_status(:redirect)
348
+ #
349
+ # @example Works with standard `response` objects and Capybara's `page`
350
+ # expect(response).to have_http_status(404)
351
+ # expect(page).to have_http_status(:created)
352
+ #
353
+ # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
354
+ # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
355
+ def have_http_status(target)
356
+ raise ArgumentError, "Invalid HTTP status: nil" unless target
357
+ HaveHttpStatus.matcher_for_status(target)
358
+ end
359
+ end
360
+ end
361
+ end