rspec-rails 3.7.0 → 4.0.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 (81) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/Capybara.md +5 -54
  4. data/Changelog.md +259 -70
  5. data/README.md +265 -496
  6. data/lib/generators/rspec/channel/channel_generator.rb +12 -0
  7. data/lib/generators/rspec/{observer/templates/observer_spec.rb → channel/templates/channel_spec.rb.erb} +1 -1
  8. data/lib/generators/rspec/controller/controller_generator.rb +21 -4
  9. data/lib/generators/rspec/controller/templates/request_spec.rb +14 -0
  10. data/lib/generators/rspec/controller/templates/routing_spec.rb +13 -0
  11. data/lib/generators/rspec/feature/feature_generator.rb +4 -4
  12. data/lib/generators/rspec/generator/generator_generator.rb +24 -0
  13. data/lib/generators/rspec/generator/templates/generator_spec.rb +6 -0
  14. data/lib/generators/rspec/helper/helper_generator.rb +1 -1
  15. data/lib/generators/rspec/install/install_generator.rb +4 -4
  16. data/lib/generators/rspec/install/templates/spec/rails_helper.rb +25 -12
  17. data/lib/generators/rspec/integration/integration_generator.rb +4 -4
  18. data/lib/generators/rspec/integration/templates/request_spec.rb +1 -1
  19. data/lib/generators/rspec/mailbox/mailbox_generator.rb +14 -0
  20. data/lib/generators/rspec/mailbox/templates/mailbox_spec.rb.erb +7 -0
  21. data/lib/generators/rspec/mailer/mailer_generator.rb +2 -1
  22. data/lib/generators/rspec/mailer/templates/preview.rb +1 -1
  23. data/lib/generators/rspec/model/model_generator.rb +6 -5
  24. data/lib/generators/rspec/model/templates/fixtures.yml +1 -1
  25. data/lib/generators/rspec/request/request_generator.rb +1 -1
  26. data/lib/generators/rspec/scaffold/scaffold_generator.rb +43 -23
  27. data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +2 -38
  28. data/lib/generators/rspec/scaffold/templates/api_request_spec.rb +131 -0
  29. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +17 -17
  30. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +1 -1
  31. data/lib/generators/rspec/scaffold/templates/index_spec.rb +2 -2
  32. data/lib/generators/rspec/scaffold/templates/new_spec.rb +1 -1
  33. data/lib/generators/rspec/scaffold/templates/request_spec.rb +133 -0
  34. data/lib/generators/rspec/scaffold/templates/routing_spec.rb +10 -13
  35. data/lib/generators/rspec/scaffold/templates/show_spec.rb +1 -1
  36. data/lib/generators/rspec/system/system_generator.rb +26 -0
  37. data/lib/generators/rspec/system/templates/system_spec.rb +9 -0
  38. data/lib/generators/rspec/view/view_generator.rb +2 -2
  39. data/lib/generators/rspec.rb +0 -6
  40. data/lib/rspec/rails/adapters.rb +11 -76
  41. data/lib/rspec/rails/configuration.rb +47 -36
  42. data/lib/rspec/rails/example/channel_example_group.rb +93 -0
  43. data/lib/rspec/rails/example/controller_example_group.rb +4 -4
  44. data/lib/rspec/rails/example/feature_example_group.rb +6 -26
  45. data/lib/rspec/rails/example/helper_example_group.rb +2 -9
  46. data/lib/rspec/rails/example/mailbox_example_group.rb +80 -0
  47. data/lib/rspec/rails/example/mailer_example_group.rb +1 -1
  48. data/lib/rspec/rails/example/rails_example_group.rb +1 -1
  49. data/lib/rspec/rails/example/system_example_group.rb +96 -60
  50. data/lib/rspec/rails/example/view_example_group.rb +47 -28
  51. data/lib/rspec/rails/example.rb +3 -3
  52. data/lib/rspec/rails/extensions/active_record/proxy.rb +1 -9
  53. data/lib/rspec/rails/feature_check.rb +12 -29
  54. data/lib/rspec/rails/fixture_file_upload_support.rb +40 -0
  55. data/lib/rspec/rails/fixture_support.rb +37 -31
  56. data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +170 -0
  57. data/lib/rspec/rails/matchers/action_cable/have_streams.rb +58 -0
  58. data/lib/rspec/rails/matchers/action_cable.rb +65 -0
  59. data/lib/rspec/rails/matchers/action_mailbox.rb +64 -0
  60. data/lib/rspec/rails/matchers/active_job.rb +180 -22
  61. data/lib/rspec/rails/matchers/base_matcher.rb +179 -0
  62. data/lib/rspec/rails/matchers/be_a_new.rb +1 -1
  63. data/lib/rspec/rails/matchers/be_new_record.rb +1 -1
  64. data/lib/rspec/rails/matchers/be_valid.rb +1 -1
  65. data/lib/rspec/rails/matchers/have_enqueued_mail.rb +198 -0
  66. data/lib/rspec/rails/matchers/have_http_status.rb +34 -13
  67. data/lib/rspec/rails/matchers/have_rendered.rb +2 -1
  68. data/lib/rspec/rails/matchers/redirect_to.rb +1 -1
  69. data/lib/rspec/rails/matchers/routing_matchers.rb +14 -14
  70. data/lib/rspec/rails/matchers.rb +11 -0
  71. data/lib/rspec/rails/tasks/rspec.rake +7 -17
  72. data/lib/rspec/rails/vendor/capybara.rb +10 -15
  73. data/lib/rspec/rails/version.rb +1 -1
  74. data/lib/rspec/rails/view_path_builder.rb +1 -1
  75. data/lib/rspec/rails/view_rendering.rb +16 -5
  76. data/lib/rspec/rails.rb +1 -0
  77. data/lib/rspec-rails.rb +13 -10
  78. data.tar.gz.sig +0 -0
  79. metadata +55 -33
  80. metadata.gz.sig +0 -0
  81. data/lib/generators/rspec/observer/observer_generator.rb +0 -13
@@ -0,0 +1,179 @@
1
+ module RSpec
2
+ module Rails
3
+ module Matchers
4
+ # @api private
5
+ #
6
+ # Base class to build matchers. Should not be instantiated directly.
7
+ class BaseMatcher
8
+ include RSpec::Matchers::Composable
9
+
10
+ # @api private
11
+ # Used to detect when no arg is passed to `initialize`.
12
+ # `nil` cannot be used because it's a valid value to pass.
13
+ UNDEFINED = Object.new.freeze
14
+
15
+ # @private
16
+ attr_reader :actual, :expected, :rescued_exception
17
+
18
+ # @private
19
+ attr_writer :matcher_name
20
+
21
+ def initialize(expected = UNDEFINED)
22
+ @expected = expected unless UNDEFINED.equal?(expected)
23
+ end
24
+
25
+ # @api private
26
+ # Indicates if the match is successful. Delegates to `match`, which
27
+ # should be defined on a subclass. Takes care of consistently
28
+ # initializing the `actual` attribute.
29
+ def matches?(actual)
30
+ @actual = actual
31
+ match(expected, actual)
32
+ end
33
+
34
+ # @api private
35
+ # Used to wrap a block of code that will indicate failure by
36
+ # raising one of the named exceptions.
37
+ #
38
+ # This is used by rspec-rails for some of its matchers that
39
+ # wrap rails' assertions.
40
+ def match_unless_raises(*exceptions)
41
+ exceptions.unshift Exception if exceptions.empty?
42
+ begin
43
+ yield
44
+ true
45
+ rescue *exceptions => @rescued_exception
46
+ false
47
+ end
48
+ end
49
+
50
+ # @api private
51
+ # Generates a description using {RSpec::Matchers::EnglishPhrasing}.
52
+ # @return [String]
53
+ def description
54
+ desc = RSpec::Matchers::EnglishPhrasing.split_words(self.class.matcher_name)
55
+ desc << RSpec::Matchers::EnglishPhrasing.list(@expected) if defined?(@expected)
56
+ desc
57
+ end
58
+
59
+ # @api private
60
+ # Matchers are not diffable by default. Override this to make your
61
+ # subclass diffable.
62
+ def diffable?
63
+ false
64
+ end
65
+
66
+ # @api private
67
+ # Most matchers are value matchers (i.e. meant to work with `expect(value)`)
68
+ # rather than block matchers (i.e. meant to work with `expect { }`), so
69
+ # this defaults to false. Block matchers must override this to return true.
70
+ def supports_block_expectations?
71
+ false
72
+ end
73
+
74
+ # @api private
75
+ def expects_call_stack_jump?
76
+ false
77
+ end
78
+
79
+ # @private
80
+ def expected_formatted
81
+ RSpec::Support::ObjectFormatter.format(@expected)
82
+ end
83
+
84
+ # @private
85
+ def actual_formatted
86
+ RSpec::Support::ObjectFormatter.format(@actual)
87
+ end
88
+
89
+ # @private
90
+ def self.matcher_name
91
+ @matcher_name ||= underscore(name.split('::').last)
92
+ end
93
+
94
+ # @private
95
+ def matcher_name
96
+ if defined?(@matcher_name)
97
+ @matcher_name
98
+ else
99
+ self.class.matcher_name
100
+ end
101
+ end
102
+
103
+ # @private
104
+ # Borrowed from ActiveSupport.
105
+ def self.underscore(camel_cased_word)
106
+ word = camel_cased_word.to_s.dup
107
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
108
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
109
+ word.tr!('-', '_')
110
+ word.downcase!
111
+ word
112
+ end
113
+ private_class_method :underscore
114
+
115
+ private
116
+
117
+ def assert_ivars(*expected_ivars)
118
+ return unless (expected_ivars - present_ivars).any?
119
+
120
+ ivar_list = RSpec::Matchers::EnglishPhrasing.list(expected_ivars)
121
+ raise "#{self.class.name} needs to supply#{ivar_list}"
122
+ end
123
+
124
+ alias present_ivars instance_variables
125
+
126
+ # @private
127
+ module HashFormatting
128
+ # `{ :a => 5, :b => 2 }.inspect` produces:
129
+ #
130
+ # {:a=>5, :b=>2}
131
+ #
132
+ # ...but it looks much better as:
133
+ #
134
+ # {:a => 5, :b => 2}
135
+ #
136
+ # This is idempotent and safe to run on a string multiple times.
137
+ def improve_hash_formatting(inspect_string)
138
+ inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
139
+ end
140
+ module_function :improve_hash_formatting
141
+ end
142
+
143
+ include HashFormatting
144
+
145
+ # @api private
146
+ # Provides default implementations of failure messages, based on the `description`.
147
+ module DefaultFailureMessages
148
+ # @api private
149
+ # Provides a good generic failure message. Based on `description`.
150
+ # When subclassing, if you are not satisfied with this failure message
151
+ # you often only need to override `description`.
152
+ # @return [String]
153
+ def failure_message
154
+ "expected #{description_of @actual} to #{description}".dup
155
+ end
156
+
157
+ # @api private
158
+ # Provides a good generic negative failure message. Based on `description`.
159
+ # When subclassing, if you are not satisfied with this failure message
160
+ # you often only need to override `description`.
161
+ # @return [String]
162
+ def failure_message_when_negated
163
+ "expected #{description_of @actual} not to #{description}".dup
164
+ end
165
+
166
+ # @private
167
+ def self.has_default_failure_messages?(matcher)
168
+ matcher.method(:failure_message).owner == self &&
169
+ matcher.method(:failure_message_when_negated).owner == self
170
+ rescue NameError
171
+ false
172
+ end
173
+ end
174
+
175
+ include DefaultFailureMessages
176
+ end
177
+ end
178
+ end
179
+ end
@@ -6,7 +6,7 @@ module RSpec
6
6
  # Matcher class for `be_a_new`. Should not be instantiated directly.
7
7
  #
8
8
  # @see RSpec::Rails::Matchers#be_a_new
9
- class BeANew < RSpec::Matchers::BuiltIn::BaseMatcher
9
+ class BeANew < RSpec::Rails::Matchers::BaseMatcher
10
10
  # @private
11
11
  def initialize(expected)
12
12
  @expected = expected
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Rails
3
3
  module Matchers
4
4
  # @private
5
- class BeANewRecord < RSpec::Matchers::BuiltIn::BaseMatcher
5
+ class BeANewRecord < RSpec::Rails::Matchers::BaseMatcher
6
6
  def matches?(actual)
7
7
  actual.new_record?
8
8
  end
@@ -15,7 +15,7 @@ module RSpec
15
15
  def failure_message
16
16
  message = "expected #{actual.inspect} to be valid"
17
17
 
18
- if actual.respond_to?(:errors)
18
+ if actual.respond_to?(:errors) && actual.method(:errors).arity < 1
19
19
  errors = if actual.errors.respond_to?(:full_messages)
20
20
  actual.errors.full_messages
21
21
  else
@@ -0,0 +1,198 @@
1
+ # We require the minimum amount of rspec-mocks possible to avoid
2
+ # conflicts with other mocking frameworks.
3
+ # See: https://github.com/rspec/rspec-rails/issues/2252
4
+ require "rspec/mocks/argument_matchers"
5
+ require "rspec/rails/matchers/active_job"
6
+
7
+ module RSpec
8
+ module Rails
9
+ module Matchers
10
+ # Matcher class for `have_enqueued_mail`. Should not be instantiated directly.
11
+ #
12
+ # @private
13
+ # @see RSpec::Rails::Matchers#have_enqueued_mail
14
+ class HaveEnqueuedMail < ActiveJob::HaveEnqueuedJob
15
+ MAILER_JOB_METHOD = 'deliver_now'.freeze
16
+
17
+ include RSpec::Mocks::ArgumentMatchers
18
+
19
+ def initialize(mailer_class, method_name)
20
+ super(nil)
21
+ @mailer_class = mailer_class
22
+ @method_name = method_name
23
+ @mail_args = []
24
+ end
25
+
26
+ def description
27
+ "enqueues #{mailer_class_name}.#{@method_name}"
28
+ end
29
+
30
+ def with(*args, &block)
31
+ @mail_args = args
32
+ block.nil? ? super : super(&yield_mail_args(block))
33
+ end
34
+
35
+ def matches?(block)
36
+ raise ArgumentError, 'have_enqueued_mail and enqueue_mail only work with block arguments' unless block.respond_to?(:call)
37
+
38
+ check_active_job_adapter
39
+ super
40
+ end
41
+
42
+ def failure_message
43
+ "expected to enqueue #{base_message}".tap do |msg|
44
+ msg << "\n#{unmatching_mail_jobs_message}" if unmatching_mail_jobs.any?
45
+ end
46
+ end
47
+
48
+ def failure_message_when_negated
49
+ "expected not to enqueue #{base_message}"
50
+ end
51
+
52
+ private
53
+
54
+ def base_message
55
+ [mailer_class_name, @method_name].compact.join('.').tap do |msg|
56
+ msg << " #{expected_count_message}"
57
+ msg << " with #{@mail_args}," if @mail_args.any?
58
+ msg << " on queue #{@queue}," if @queue
59
+ msg << " at #{@at.inspect}," if @at
60
+ msg << " but enqueued #{@matching_jobs.size}"
61
+ end
62
+ end
63
+
64
+ def expected_count_message
65
+ "#{message_expectation_modifier} #{@expected_number} #{@expected_number == 1 ? 'time' : 'times'}"
66
+ end
67
+
68
+ def mailer_class_name
69
+ @mailer_class ? @mailer_class.name : 'ActionMailer::Base'
70
+ end
71
+
72
+ def job_match?(job)
73
+ legacy_mail?(job) || parameterized_mail?(job) || unified_mail?(job)
74
+ end
75
+
76
+ def arguments_match?(job)
77
+ @args =
78
+ if @mail_args.any?
79
+ base_mailer_args + @mail_args
80
+ elsif @mailer_class && @method_name
81
+ base_mailer_args + [any_args]
82
+ elsif @mailer_class
83
+ [mailer_class_name, any_args]
84
+ else
85
+ []
86
+ end
87
+
88
+ super(job)
89
+ end
90
+
91
+ def base_mailer_args
92
+ [mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
93
+ end
94
+
95
+ def yield_mail_args(block)
96
+ proc { |*job_args| block.call(*(job_args - base_mailer_args)) }
97
+ end
98
+
99
+ def check_active_job_adapter
100
+ return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
101
+
102
+ raise StandardError, "To use HaveEnqueuedMail matcher set `ActiveJob::Base.queue_adapter = :test`"
103
+ end
104
+
105
+ def unmatching_mail_jobs
106
+ @unmatching_jobs.select do |job|
107
+ job_match?(job)
108
+ end
109
+ end
110
+
111
+ def unmatching_mail_jobs_message
112
+ msg = "Queued deliveries:"
113
+
114
+ unmatching_mail_jobs.each do |job|
115
+ msg << "\n #{mail_job_message(job)}"
116
+ end
117
+
118
+ msg
119
+ end
120
+
121
+ def mail_job_message(job)
122
+ mailer_method = job[:args][0..1].join('.')
123
+
124
+ mailer_args = job[:args][3..-1]
125
+ msg_parts = []
126
+ msg_parts << "with #{mailer_args}" if mailer_args.any?
127
+ msg_parts << "on queue #{job[:queue]}" if job[:queue] && job[:queue] != 'mailers'
128
+ msg_parts << "at #{Time.at(job[:at])}" if job[:at]
129
+
130
+ "#{mailer_method} #{msg_parts.join(', ')}".strip
131
+ end
132
+
133
+ def legacy_mail?(job)
134
+ job[:job] == ActionMailer::DeliveryJob
135
+ end
136
+
137
+ def parameterized_mail?(job)
138
+ RSpec::Rails::FeatureCheck.has_action_mailer_parameterized? && job[:job] == ActionMailer::Parameterized::DeliveryJob
139
+ end
140
+
141
+ def unified_mail?(job)
142
+ RSpec::Rails::FeatureCheck.has_action_mailer_unified_delivery? && job[:job] == ActionMailer::MailDeliveryJob
143
+ end
144
+ end
145
+ # @api public
146
+ # Passes if an email has been enqueued inside block.
147
+ # May chain with to specify expected arguments.
148
+ # May chain at_least, at_most or exactly to specify a number of times.
149
+ # May chain at to specify a send time.
150
+ # May chain on_queue to specify a queue.
151
+ #
152
+ # @example
153
+ # expect {
154
+ # MyMailer.welcome(user).deliver_later
155
+ # }.to have_enqueued_mail
156
+ #
157
+ # expect {
158
+ # MyMailer.welcome(user).deliver_later
159
+ # }.to have_enqueued_mail(MyMailer)
160
+ #
161
+ # expect {
162
+ # MyMailer.welcome(user).deliver_later
163
+ # }.to have_enqueued_mail(MyMailer, :welcome)
164
+ #
165
+ # # Using alias
166
+ # expect {
167
+ # MyMailer.welcome(user).deliver_later
168
+ # }.to enqueue_mail(MyMailer, :welcome)
169
+ #
170
+ # expect {
171
+ # MyMailer.welcome(user).deliver_later
172
+ # }.to have_enqueued_mail(MyMailer, :welcome).with(user)
173
+ #
174
+ # expect {
175
+ # MyMailer.welcome(user).deliver_later
176
+ # MyMailer.welcome(user).deliver_later
177
+ # }.to have_enqueued_mail(MyMailer, :welcome).at_least(:once)
178
+ #
179
+ # expect {
180
+ # MyMailer.welcome(user).deliver_later
181
+ # }.to have_enqueued_mail(MyMailer, :welcome).at_most(:twice)
182
+ #
183
+ # expect {
184
+ # MyMailer.welcome(user).deliver_later(wait_until: Date.tomorrow.noon)
185
+ # }.to have_enqueued_mail(MyMailer, :welcome).at(Date.tomorrow.noon)
186
+ #
187
+ # expect {
188
+ # MyMailer.welcome(user).deliver_later(queue: :urgent_mail)
189
+ # }.to have_enqueued_mail(MyMailer, :welcome).on_queue(:urgent_mail)
190
+ def have_enqueued_mail(mailer_class = nil, mail_method_name = nil)
191
+ HaveEnqueuedMail.new(mailer_class, mail_method_name)
192
+ end
193
+ alias_method :have_enqueued_email, :have_enqueued_mail
194
+ alias_method :enqueue_mail, :have_enqueued_mail
195
+ alias_method :enqueue_email, :have_enqueued_mail
196
+ end
197
+ end
198
+ end
@@ -59,6 +59,7 @@ module RSpec
59
59
  # `@invalid_response` is present, `nil` otherwise
60
60
  def invalid_response_type_message
61
61
  return unless @invalid_response
62
+
62
63
  "expected a response object, but an instance of " \
63
64
  "#{@invalid_response.class} was received"
64
65
  end
@@ -72,8 +73,8 @@ module RSpec
72
73
  # @example
73
74
  # expect(response).to have_http_status(404)
74
75
  #
75
- # @see RSpec::Rails::Matchers.have_http_status
76
- class NumericCode < RSpec::Matchers::BuiltIn::BaseMatcher
76
+ # @see RSpec::Rails::Matchers#have_http_status
77
+ class NumericCode < RSpec::Rails::Matchers::BaseMatcher
77
78
  include HaveHttpStatus
78
79
 
79
80
  def initialize(code)
@@ -122,9 +123,9 @@ module RSpec
122
123
  # @example
123
124
  # expect(response).to have_http_status(:created)
124
125
  #
125
- # @see RSpec::Rails::Matchers.have_http_status
126
+ # @see RSpec::Rails::Matchers#have_http_status
126
127
  # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
127
- class SymbolicStatus < RSpec::Matchers::BuiltIn::BaseMatcher
128
+ class SymbolicStatus < RSpec::Rails::Matchers::BaseMatcher
128
129
  include HaveHttpStatus
129
130
 
130
131
  def initialize(status)
@@ -174,6 +175,7 @@ module RSpec
174
175
  # @return [Symbol] representing the actual http numeric code
175
176
  def actual_status
176
177
  return unless actual
178
+
177
179
  @actual_status ||= compute_status_from(actual)
178
180
  end
179
181
 
@@ -234,33 +236,38 @@ module RSpec
234
236
  # expect(response).to have_http_status(:missing)
235
237
  # expect(response).to have_http_status(:redirect)
236
238
  #
237
- # @see RSpec::Rails::Matchers.have_http_status
238
- # @see ActionDispatch::TestResponse
239
- class GenericStatus < RSpec::Matchers::BuiltIn::BaseMatcher
239
+ # @see RSpec::Rails::Matchers#have_http_status
240
+ # @see https://github.com/rails/rails/blob/6-0-stable/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
241
+ class GenericStatus < RSpec::Rails::Matchers::BaseMatcher
240
242
  include HaveHttpStatus
241
243
 
242
244
  # @return [Array<Symbol>] of status codes which represent a HTTP status
243
245
  # code "group"
244
246
  # @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
245
247
  def self.valid_statuses
246
- [:error, :success, :missing, :redirect]
248
+ [
249
+ :error, :success, :missing,
250
+ :server_error, :successful, :not_found,
251
+ :redirect
252
+ ]
247
253
  end
248
254
 
249
255
  def initialize(type)
250
256
  unless self.class.valid_statuses.include?(type)
251
257
  raise ArgumentError, "Invalid generic HTTP status: #{type.inspect}"
252
258
  end
259
+
253
260
  @expected = type
254
261
  @actual = nil
255
262
  @invalid_response = nil
256
263
  end
257
264
 
258
265
  # @return [Boolean] `true` if Rack's associated numeric HTTP code matched
259
- # the `response` code
266
+ # the `response` code or the named response status
260
267
  def matches?(response)
261
268
  test_response = as_test_response(response)
262
269
  @actual = test_response.response_code
263
- test_response.send("#{expected}?")
270
+ check_expected_status(test_response, expected)
264
271
  rescue TypeError => _ignored
265
272
  @invalid_response = response
266
273
  false
@@ -283,6 +290,19 @@ module RSpec
283
290
  "expected the response not to have #{type_message} but it was #{actual}"
284
291
  end
285
292
 
293
+ protected
294
+
295
+ RESPONSE_METHODS = {
296
+ success: 'successful',
297
+ error: 'server_error',
298
+ missing: 'not_found'
299
+ }.freeze
300
+
301
+ def check_expected_status(test_response, expected)
302
+ test_response.send(
303
+ "#{RESPONSE_METHODS.fetch(expected, expected)}?")
304
+ end
305
+
286
306
  private
287
307
 
288
308
  # @return [String] formating the expected status and associated code(s)
@@ -316,11 +336,11 @@ module RSpec
316
336
  # @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/http/response.rb#L74
317
337
  # @see https://github.com/rack/rack/blob/ce4a3959/lib/rack/response.rb#L119-L122
318
338
  @type_codes ||= case expected
319
- when :error
339
+ when :error, :server_error
320
340
  "5xx"
321
- when :success
341
+ when :success, :successful
322
342
  "2xx"
323
- when :missing
343
+ when :missing, :not_found
324
344
  "404"
325
345
  when :redirect
326
346
  "3xx"
@@ -357,6 +377,7 @@ module RSpec
357
377
  # @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
358
378
  def have_http_status(target)
359
379
  raise ArgumentError, "Invalid HTTP status: nil" unless target
380
+
360
381
  HaveHttpStatus.matcher_for_status(target)
361
382
  end
362
383
  end
@@ -4,7 +4,7 @@ module RSpec
4
4
  # Matcher for template rendering.
5
5
  module RenderTemplate
6
6
  # @private
7
- class RenderTemplateMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
7
+ class RenderTemplateMatcher < RSpec::Rails::Matchers::BaseMatcher
8
8
  def initialize(scope, expected, message = nil)
9
9
  @expected = Symbol === expected ? expected.to_s : expected
10
10
  @message = message
@@ -29,6 +29,7 @@ module RSpec
29
29
  def check_redirect
30
30
  response = @scope.response
31
31
  return unless response.respond_to?(:redirect?) && response.redirect?
32
+
32
33
  @redirect_is = @scope.send(:normalize_argument_to_redirection, response.location)
33
34
  end
34
35
 
@@ -4,7 +4,7 @@ module RSpec
4
4
  # Matcher for redirects.
5
5
  module RedirectTo
6
6
  # @private
7
- class RedirectTo < RSpec::Matchers::BuiltIn::BaseMatcher
7
+ class RedirectTo < RSpec::Rails::Matchers::BaseMatcher
8
8
  def initialize(scope, expected)
9
9
  @expected = expected
10
10
  @scope = scope
@@ -6,7 +6,7 @@ module RSpec
6
6
  extend RSpec::Matchers::DSL
7
7
 
8
8
  # @private
9
- class RouteToMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
9
+ class RouteToMatcher < RSpec::Rails::Matchers::BaseMatcher
10
10
  def initialize(scope, *expected)
11
11
  @scope = scope
12
12
  @expected = expected[1] || {}
@@ -14,7 +14,7 @@ module RSpec
14
14
  @expected.merge!(expected[0])
15
15
  else
16
16
  controller, action = expected[0].split('#')
17
- @expected.merge!(:controller => controller, :action => action)
17
+ @expected.merge!(controller: controller, action: action)
18
18
  end
19
19
  end
20
20
 
@@ -26,7 +26,7 @@ module RSpec
26
26
  path, query = *verb_to_path_map.values.first.split('?')
27
27
  @scope.assert_recognizes(
28
28
  @expected,
29
- { :method => verb_to_path_map.keys.first, :path => path },
29
+ {method: verb_to_path_map.keys.first, path: path},
30
30
  Rack::Utils.parse_nested_query(query)
31
31
  )
32
32
  end
@@ -50,20 +50,20 @@ module RSpec
50
50
  #
51
51
  # @example
52
52
  #
53
- # expect(:get => "/things/special").to route_to(
54
- # :controller => "things",
55
- # :action => "special"
53
+ # expect(get: "/things/special").to route_to(
54
+ # controller: "things",
55
+ # action: "special"
56
56
  # )
57
57
  #
58
- # expect(:get => "/things/special").to route_to("things#special")
58
+ # expect(get: "/things/special").to route_to("things#special")
59
59
  #
60
- # @see http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes
60
+ # @see https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes
61
61
  def route_to(*expected)
62
62
  RouteToMatcher.new(self, *expected)
63
63
  end
64
64
 
65
65
  # @private
66
- class BeRoutableMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
66
+ class BeRoutableMatcher < RSpec::Rails::Matchers::BaseMatcher
67
67
  def initialize(scope)
68
68
  @scope = scope
69
69
  end
@@ -72,7 +72,7 @@ module RSpec
72
72
  @actual = path
73
73
  match_unless_raises ActionController::RoutingError do
74
74
  @routing_options = @scope.routes.recognize_path(
75
- path.values.first, :method => path.keys.first
75
+ path.values.first, method: path.keys.first
76
76
  )
77
77
  end
78
78
  end
@@ -95,9 +95,9 @@ module RSpec
95
95
  # `RouteSet#recognize_path`.
96
96
  #
97
97
  # @example You can use route helpers provided by rspec-rails.
98
- # expect(:get => "/a/path").to be_routable
99
- # expect(:post => "/another/path").to be_routable
100
- # expect(:put => "/yet/another/path").to be_routable
98
+ # expect(get: "/a/path").to be_routable
99
+ # expect(post: "/another/path").to be_routable
100
+ # expect(put: "/yet/another/path").to be_routable
101
101
  def be_routable
102
102
  BeRoutableMatcher.new(self)
103
103
  end
@@ -115,7 +115,7 @@ module RSpec
115
115
  # Shorthand method for matching this type of route.
116
116
  %w[get post put patch delete options head].each do |method|
117
117
  define_method method do |path|
118
- { method.to_sym => path }
118
+ {method.to_sym => path}
119
119
  end
120
120
  end
121
121
  end
@@ -11,6 +11,7 @@ module RSpec
11
11
  end
12
12
  end
13
13
 
14
+ require 'rspec/rails/matchers/base_matcher'
14
15
  require 'rspec/rails/matchers/have_rendered'
15
16
  require 'rspec/rails/matchers/redirect_to'
16
17
  require 'rspec/rails/matchers/routing_matchers'
@@ -19,6 +20,16 @@ require 'rspec/rails/matchers/be_a_new'
19
20
  require 'rspec/rails/matchers/relation_match_array'
20
21
  require 'rspec/rails/matchers/be_valid'
21
22
  require 'rspec/rails/matchers/have_http_status'
23
+
22
24
  if RSpec::Rails::FeatureCheck.has_active_job?
23
25
  require 'rspec/rails/matchers/active_job'
26
+ require 'rspec/rails/matchers/have_enqueued_mail'
27
+ end
28
+
29
+ if RSpec::Rails::FeatureCheck.has_action_cable_testing?
30
+ require 'rspec/rails/matchers/action_cable'
31
+ end
32
+
33
+ if RSpec::Rails::FeatureCheck.has_action_mailbox?
34
+ require 'rspec/rails/matchers/action_mailbox'
24
35
  end