rspec-rails 5.1.2 → 7.1.1
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Changelog.md +355 -173
- data/README.md +40 -40
- data/lib/generators/rspec/channel/channel_generator.rb +1 -1
- data/lib/generators/rspec/controller/controller_generator.rb +4 -4
- data/lib/generators/rspec/feature/feature_generator.rb +1 -1
- data/lib/generators/rspec/generator/generator_generator.rb +3 -3
- data/lib/generators/rspec/generator/templates/generator_spec.rb +1 -2
- data/lib/generators/rspec/helper/helper_generator.rb +1 -1
- data/lib/generators/rspec/install/install_generator.rb +19 -2
- data/lib/generators/rspec/install/templates/spec/rails_helper.rb +27 -15
- data/lib/generators/rspec/job/job_generator.rb +1 -1
- data/lib/generators/rspec/mailbox/mailbox_generator.rb +1 -1
- data/lib/generators/rspec/mailer/mailer_generator.rb +5 -3
- data/lib/generators/rspec/mailer/templates/preview.rb +3 -3
- data/lib/generators/rspec/model/model_generator.rb +3 -3
- data/lib/generators/rspec/request/request_generator.rb +10 -3
- data/lib/generators/rspec/scaffold/scaffold_generator.rb +4 -4
- data/lib/generators/rspec/scaffold/templates/controller_spec.rb +4 -4
- data/lib/generators/rspec/scaffold/templates/edit_spec.rb +8 -4
- data/lib/generators/rspec/scaffold/templates/index_spec.rb +2 -1
- data/lib/generators/rspec/scaffold/templates/new_spec.rb +1 -1
- data/lib/generators/rspec/scaffold/templates/request_spec.rb +4 -4
- data/lib/generators/rspec/scaffold/templates/show_spec.rb +1 -1
- data/lib/generators/rspec/system/system_generator.rb +1 -1
- data/lib/generators/rspec/view/view_generator.rb +2 -2
- data/lib/generators/rspec.rb +18 -1
- data/lib/rspec/rails/adapters.rb +11 -0
- data/lib/rspec/rails/configuration.rb +43 -14
- data/lib/rspec/rails/example/mailbox_example_group.rb +1 -1
- data/lib/rspec/rails/example/rails_example_group.rb +6 -0
- data/lib/rspec/rails/example/routing_example_group.rb +0 -2
- data/lib/rspec/rails/example/system_example_group.rb +67 -12
- data/lib/rspec/rails/example/view_example_group.rb +6 -5
- data/lib/rspec/rails/feature_check.rb +6 -2
- data/lib/rspec/rails/file_fixture_support.rb +3 -0
- data/lib/rspec/rails/fixture_file_upload_support.rb +20 -31
- data/lib/rspec/rails/fixture_support.rb +44 -17
- data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +16 -6
- data/lib/rspec/rails/matchers/action_cable.rb +6 -1
- data/lib/rspec/rails/matchers/active_job.rb +73 -12
- data/lib/rspec/rails/matchers/have_enqueued_mail.rb +40 -7
- data/lib/rspec/rails/matchers/have_http_status.rb +4 -8
- data/lib/rspec/rails/matchers/routing_matchers.rb +2 -2
- data/lib/rspec/rails/matchers/send_email.rb +122 -0
- data/lib/rspec/rails/matchers.rb +1 -0
- data/lib/rspec/rails/tasks/rspec.rake +3 -1
- data/lib/rspec/rails/vendor/capybara.rb +1 -3
- data/lib/rspec/rails/version.rb +1 -1
- data/lib/rspec/rails/view_assigns.rb +0 -18
- data/lib/rspec/rails/view_rendering.rb +13 -11
- data/lib/rspec-rails.rb +28 -7
- data.tar.gz.sig +0 -0
- metadata +42 -58
- metadata.gz.sig +0 -0
- data/lib/generators/rspec/integration/integration_generator.rb +0 -22
- /data/lib/generators/rspec/{integration → request}/templates/request_spec.rb +0 -0
@@ -14,6 +14,7 @@ module RSpec
|
|
14
14
|
def initialize
|
15
15
|
@args = []
|
16
16
|
@queue = nil
|
17
|
+
@priority = nil
|
17
18
|
@at = nil
|
18
19
|
@block = proc { }
|
19
20
|
set_expected_number(:exactly, 1)
|
@@ -30,6 +31,11 @@ module RSpec
|
|
30
31
|
self
|
31
32
|
end
|
32
33
|
|
34
|
+
def at_priority(priority)
|
35
|
+
@priority = priority.to_i
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
33
39
|
def at(time_or_date)
|
34
40
|
case time_or_date
|
35
41
|
when Time then @at = Time.at(time_or_date.to_f)
|
@@ -71,6 +77,8 @@ module RSpec
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def failure_message
|
80
|
+
return @failure_message if defined?(@failure_message)
|
81
|
+
|
74
82
|
"expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
|
75
83
|
if @unmatching_jobs.any?
|
76
84
|
msg << "\nQueued jobs:"
|
@@ -101,7 +109,7 @@ module RSpec
|
|
101
109
|
|
102
110
|
def check(jobs)
|
103
111
|
@matching_jobs, @unmatching_jobs = jobs.partition do |job|
|
104
|
-
if
|
112
|
+
if matches_constraints?(job)
|
105
113
|
args = deserialize_arguments(job)
|
106
114
|
@block.call(*args)
|
107
115
|
true
|
@@ -109,6 +117,12 @@ module RSpec
|
|
109
117
|
false
|
110
118
|
end
|
111
119
|
end
|
120
|
+
|
121
|
+
if (signature_mismatch = detect_args_signature_mismatch(@matching_jobs))
|
122
|
+
@failure_message = signature_mismatch
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
112
126
|
@matching_jobs_count = @matching_jobs.size
|
113
127
|
|
114
128
|
case @expectation_type
|
@@ -123,6 +137,7 @@ module RSpec
|
|
123
137
|
msg << " with #{@args}," if @args.any?
|
124
138
|
msg << " on queue #{@queue}," if @queue
|
125
139
|
msg << " at #{@at.inspect}," if @at
|
140
|
+
msg << " with priority #{@priority}," if @priority
|
126
141
|
msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
|
127
142
|
end
|
128
143
|
end
|
@@ -132,13 +147,23 @@ module RSpec
|
|
132
147
|
msg_parts << "with #{deserialize_arguments(job)}" if job[:args].any?
|
133
148
|
msg_parts << "on queue #{job[:queue]}" if job[:queue]
|
134
149
|
msg_parts << "at #{Time.at(job[:at])}" if job[:at]
|
150
|
+
msg_parts <<
|
151
|
+
if job[:priority]
|
152
|
+
"with priority #{job[:priority]}"
|
153
|
+
else
|
154
|
+
"with no priority specified"
|
155
|
+
end
|
135
156
|
|
136
157
|
"#{job[:job].name} job".tap do |msg|
|
137
158
|
msg << " #{msg_parts.join(', ')}" if msg_parts.any?
|
138
159
|
end
|
139
160
|
end
|
140
161
|
|
141
|
-
def
|
162
|
+
def matches_constraints?(job)
|
163
|
+
job_matches?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job) && priority_match?(job)
|
164
|
+
end
|
165
|
+
|
166
|
+
def job_matches?(job)
|
142
167
|
@job ? @job == job[:job] : true
|
143
168
|
end
|
144
169
|
|
@@ -152,12 +177,48 @@ module RSpec
|
|
152
177
|
end
|
153
178
|
end
|
154
179
|
|
180
|
+
def detect_args_signature_mismatch(jobs)
|
181
|
+
return if skip_signature_verification?
|
182
|
+
|
183
|
+
jobs.each do |job|
|
184
|
+
args = deserialize_arguments(job)
|
185
|
+
|
186
|
+
if (signature_mismatch = check_args_signature_mismatch(job.fetch(:job), :perform, args))
|
187
|
+
return signature_mismatch
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
|
194
|
+
def skip_signature_verification?
|
195
|
+
return true unless defined?(::RSpec::Mocks) && (::RSpec::Mocks.respond_to?(:configuration))
|
196
|
+
|
197
|
+
!RSpec::Mocks.configuration.verify_partial_doubles? ||
|
198
|
+
RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification
|
199
|
+
end
|
200
|
+
|
201
|
+
def check_args_signature_mismatch(job_class, job_method, args)
|
202
|
+
signature = Support::MethodSignature.new(job_class.public_instance_method(job_method))
|
203
|
+
verifier = Support::StrictSignatureVerifier.new(signature, args)
|
204
|
+
|
205
|
+
unless verifier.valid?
|
206
|
+
"Incorrect arguments passed to #{job_class.name}: #{verifier.error_message}"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
155
210
|
def queue_match?(job)
|
156
211
|
return true unless @queue
|
157
212
|
|
158
213
|
@queue == job[:queue]
|
159
214
|
end
|
160
215
|
|
216
|
+
def priority_match?(job)
|
217
|
+
return true unless @priority
|
218
|
+
|
219
|
+
@priority == job[:priority]
|
220
|
+
end
|
221
|
+
|
161
222
|
def at_match?(job)
|
162
223
|
return true unless @at
|
163
224
|
return job[:at].nil? if @at == :no_wait
|
@@ -181,7 +242,7 @@ module RSpec
|
|
181
242
|
|`Time.current.change(usec: 0)`
|
182
243
|
|
|
183
244
|
|Note: RSpec cannot do this for you because jobs can be scheduled with usec
|
184
|
-
|precision and we do not know
|
245
|
+
|precision and we do not know whether it is on purpose or not.
|
185
246
|
|
|
186
247
|
|
|
187
248
|
WARNING
|
@@ -230,11 +291,11 @@ module RSpec
|
|
230
291
|
def matches?(proc)
|
231
292
|
raise ArgumentError, "have_enqueued_job and enqueue_job only support block expectations" unless Proc === proc
|
232
293
|
|
233
|
-
|
294
|
+
original_enqueued_jobs = Set.new(queue_adapter.enqueued_jobs)
|
234
295
|
proc.call
|
235
|
-
|
296
|
+
enqueued_jobs = Set.new(queue_adapter.enqueued_jobs)
|
236
297
|
|
237
|
-
check(
|
298
|
+
check(enqueued_jobs - original_enqueued_jobs)
|
238
299
|
end
|
239
300
|
|
240
301
|
def does_not_match?(proc)
|
@@ -384,33 +445,33 @@ module RSpec
|
|
384
445
|
#
|
385
446
|
# @example
|
386
447
|
# expect {
|
387
|
-
#
|
448
|
+
# perform_enqueued_jobs { HeavyLiftingJob.perform_later }
|
388
449
|
# }.to have_performed_job
|
389
450
|
#
|
390
451
|
# expect {
|
391
|
-
#
|
452
|
+
# perform_enqueued_jobs {
|
392
453
|
# HelloJob.perform_later
|
393
454
|
# HeavyLiftingJob.perform_later
|
394
455
|
# }
|
395
456
|
# }.to have_performed_job(HelloJob).exactly(:once)
|
396
457
|
#
|
397
458
|
# expect {
|
398
|
-
#
|
459
|
+
# perform_enqueued_jobs { 3.times { HelloJob.perform_later } }
|
399
460
|
# }.to have_performed_job(HelloJob).at_least(2).times
|
400
461
|
#
|
401
462
|
# expect {
|
402
|
-
#
|
463
|
+
# perform_enqueued_jobs { HelloJob.perform_later }
|
403
464
|
# }.to have_performed_job(HelloJob).at_most(:twice)
|
404
465
|
#
|
405
466
|
# expect {
|
406
|
-
#
|
467
|
+
# perform_enqueued_jobs {
|
407
468
|
# HelloJob.perform_later
|
408
469
|
# HeavyLiftingJob.perform_later
|
409
470
|
# }
|
410
471
|
# }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
|
411
472
|
#
|
412
473
|
# expect {
|
413
|
-
#
|
474
|
+
# perform_enqueued_jobs {
|
414
475
|
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
415
476
|
# }
|
416
477
|
# }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
|
@@ -41,6 +41,8 @@ module RSpec
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def failure_message
|
44
|
+
return @failure_message if defined?(@failure_message)
|
45
|
+
|
44
46
|
"expected to enqueue #{base_message}".tap do |msg|
|
45
47
|
msg << "\n#{unmatching_mail_jobs_message}" if unmatching_mail_jobs.any?
|
46
48
|
end
|
@@ -70,7 +72,7 @@ module RSpec
|
|
70
72
|
@mailer_class ? @mailer_class.name : 'ActionMailer::Base'
|
71
73
|
end
|
72
74
|
|
73
|
-
def
|
75
|
+
def job_matches?(job)
|
74
76
|
legacy_mail?(job) || parameterized_mail?(job) || unified_mail?(job)
|
75
77
|
end
|
76
78
|
|
@@ -89,6 +91,23 @@ module RSpec
|
|
89
91
|
super(job)
|
90
92
|
end
|
91
93
|
|
94
|
+
def detect_args_signature_mismatch(jobs)
|
95
|
+
return if @method_name.nil?
|
96
|
+
return if skip_signature_verification?
|
97
|
+
|
98
|
+
mailer_class = mailer_class_name.constantize
|
99
|
+
|
100
|
+
jobs.each do |job|
|
101
|
+
mailer_args = extract_args_without_parameterized_params(job)
|
102
|
+
|
103
|
+
if (signature_mismatch = check_args_signature_mismatch(mailer_class, @method_name, mailer_args))
|
104
|
+
return signature_mismatch
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
92
111
|
def base_mailer_args
|
93
112
|
[mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
|
94
113
|
end
|
@@ -105,18 +124,18 @@ module RSpec
|
|
105
124
|
|
106
125
|
def unmatching_mail_jobs
|
107
126
|
@unmatching_jobs.select do |job|
|
108
|
-
|
127
|
+
job_matches?(job)
|
109
128
|
end
|
110
129
|
end
|
111
130
|
|
112
131
|
def unmatching_mail_jobs_message
|
113
|
-
|
132
|
+
messages = ["Queued deliveries:"]
|
114
133
|
|
115
134
|
unmatching_mail_jobs.each do |job|
|
116
|
-
|
135
|
+
messages << " #{mail_job_message(job)}"
|
117
136
|
end
|
118
137
|
|
119
|
-
|
138
|
+
messages.join("\n")
|
120
139
|
end
|
121
140
|
|
122
141
|
def mail_job_message(job)
|
@@ -134,7 +153,7 @@ module RSpec
|
|
134
153
|
end
|
135
154
|
|
136
155
|
# Ruby 3.1 changed how params were serialized on Rails 6.1
|
137
|
-
# so we override the active job implementation and
|
156
|
+
# so we override the active job implementation and customize it here.
|
138
157
|
def deserialize_arguments(job)
|
139
158
|
args = super
|
140
159
|
|
@@ -157,8 +176,21 @@ module RSpec
|
|
157
176
|
end
|
158
177
|
end
|
159
178
|
|
179
|
+
def extract_args_without_parameterized_params(job)
|
180
|
+
args = deserialize_arguments(job)
|
181
|
+
mailer_args = args - base_mailer_args
|
182
|
+
|
183
|
+
if parameterized_mail?(job)
|
184
|
+
mailer_args = mailer_args[1..-1] # ignore parameterized params
|
185
|
+
elsif mailer_args.last.is_a?(Hash) && mailer_args.last.key?(:args)
|
186
|
+
mailer_args = args.last[:args]
|
187
|
+
end
|
188
|
+
|
189
|
+
mailer_args
|
190
|
+
end
|
191
|
+
|
160
192
|
def legacy_mail?(job)
|
161
|
-
job[:job] <= ActionMailer::DeliveryJob
|
193
|
+
RSpec::Rails::FeatureCheck.has_action_mailer_legacy_delivery_job? && job[:job] <= ActionMailer::DeliveryJob
|
162
194
|
end
|
163
195
|
|
164
196
|
def parameterized_mail?(job)
|
@@ -169,6 +201,7 @@ module RSpec
|
|
169
201
|
RSpec::Rails::FeatureCheck.has_action_mailer_unified_delivery? && job[:job] <= ActionMailer::MailDeliveryJob
|
170
202
|
end
|
171
203
|
end
|
204
|
+
|
172
205
|
# @api public
|
173
206
|
# Passes if an email has been enqueued inside block.
|
174
207
|
# May chain with to specify expected arguments.
|
@@ -33,7 +33,7 @@ module RSpec
|
|
33
33
|
# @param obj [Object] object to convert to a response
|
34
34
|
# @return [ActionDispatch::TestResponse]
|
35
35
|
def as_test_response(obj)
|
36
|
-
if ::ActionDispatch::Response === obj
|
36
|
+
if ::ActionDispatch::Response === obj || ::Rack::MockResponse === obj
|
37
37
|
::ActionDispatch::TestResponse.from_response(obj)
|
38
38
|
elsif ::ActionDispatch::TestResponse === obj
|
39
39
|
obj
|
@@ -216,11 +216,7 @@ module RSpec
|
|
216
216
|
# @see Rack::Utils::SYMBOL_TO_STATUS_CODE
|
217
217
|
# @raise [ArgumentError] if an associated code could not be found
|
218
218
|
def set_expected_code!
|
219
|
-
@expected ||=
|
220
|
-
Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(expected_status) do
|
221
|
-
raise ArgumentError,
|
222
|
-
"Invalid HTTP status: #{expected_status.inspect}"
|
223
|
-
end
|
219
|
+
@expected ||= Rack::Utils.status_code(expected_status)
|
224
220
|
end
|
225
221
|
end
|
226
222
|
|
@@ -237,7 +233,7 @@ module RSpec
|
|
237
233
|
# expect(response).to have_http_status(:redirect)
|
238
234
|
#
|
239
235
|
# @see RSpec::Rails::Matchers#have_http_status
|
240
|
-
# @see https://github.com/rails/rails/blob/
|
236
|
+
# @see https://github.com/rails/rails/blob/7-2-stable/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
|
241
237
|
class GenericStatus < RSpec::Rails::Matchers::BaseMatcher
|
242
238
|
include HaveHttpStatus
|
243
239
|
|
@@ -305,7 +301,7 @@ module RSpec
|
|
305
301
|
|
306
302
|
private
|
307
303
|
|
308
|
-
# @return [String]
|
304
|
+
# @return [String] formatting the expected status and associated code(s)
|
309
305
|
def type_message
|
310
306
|
@type_message ||= (expected == :error ? "an error" : "a #{expected}") +
|
311
307
|
" status code (#{type_codes})"
|
@@ -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
|
@@ -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
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Rails
|
5
|
+
module Matchers
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# Matcher class for `send_email`. Should not be instantiated directly.
|
9
|
+
#
|
10
|
+
# @see RSpec::Rails::Matchers#send_email
|
11
|
+
class SendEmail < RSpec::Rails::Matchers::BaseMatcher
|
12
|
+
# @api private
|
13
|
+
# Define the email attributes that should be included in the inspection output.
|
14
|
+
INSPECT_EMAIL_ATTRIBUTES = %i[subject from to cc bcc].freeze
|
15
|
+
|
16
|
+
def initialize(criteria)
|
17
|
+
@criteria = criteria
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
def supports_value_expectations?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def supports_block_expectations?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def matches?(block)
|
31
|
+
define_matched_emails(block)
|
32
|
+
|
33
|
+
@matched_emails.one?
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
# @return [String]
|
38
|
+
def failure_message
|
39
|
+
result =
|
40
|
+
if multiple_match?
|
41
|
+
"More than 1 matching emails were sent."
|
42
|
+
else
|
43
|
+
"No matching emails were sent."
|
44
|
+
end
|
45
|
+
"#{result}#{sent_emails_message}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
# @return [String]
|
50
|
+
def failure_message_when_negated
|
51
|
+
"Expected not to send an email but it was sent."
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def diffable?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def deliveries
|
61
|
+
ActionMailer::Base.deliveries
|
62
|
+
end
|
63
|
+
|
64
|
+
def define_matched_emails(block)
|
65
|
+
before = deliveries.dup
|
66
|
+
|
67
|
+
block.call
|
68
|
+
|
69
|
+
after = deliveries
|
70
|
+
|
71
|
+
@diff = after - before
|
72
|
+
@matched_emails = @diff.select(&method(:matched_email?))
|
73
|
+
end
|
74
|
+
|
75
|
+
def matched_email?(email)
|
76
|
+
@criteria.all? do |attr, value|
|
77
|
+
expected =
|
78
|
+
case attr
|
79
|
+
when :to, :from, :cc, :bcc then Array(value)
|
80
|
+
else
|
81
|
+
value
|
82
|
+
end
|
83
|
+
|
84
|
+
values_match?(expected, email.public_send(attr))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def multiple_match?
|
89
|
+
@matched_emails.many?
|
90
|
+
end
|
91
|
+
|
92
|
+
def sent_emails_message
|
93
|
+
if @diff.empty?
|
94
|
+
"\n\nThere were no any emails sent inside the expectation block."
|
95
|
+
else
|
96
|
+
sent_emails =
|
97
|
+
@diff.map do |email|
|
98
|
+
inspected = INSPECT_EMAIL_ATTRIBUTES.map { |attr| "#{attr}: #{email.public_send(attr)}" }.join(", ")
|
99
|
+
"- #{inspected}"
|
100
|
+
end.join("\n")
|
101
|
+
"\n\nThe following emails were sent:\n#{sent_emails}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @api public
|
107
|
+
# Check email sending with specific parameters.
|
108
|
+
#
|
109
|
+
# @example Positive expectation
|
110
|
+
# expect { action }.to send_email
|
111
|
+
#
|
112
|
+
# @example Negative expectations
|
113
|
+
# expect { action }.not_to send_email
|
114
|
+
#
|
115
|
+
# @example More precise expectation with attributes to match
|
116
|
+
# expect { action }.to send_email(to: 'test@example.com', subject: 'Confirm email')
|
117
|
+
def send_email(criteria = {})
|
118
|
+
SendEmail.new(criteria)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/rspec/rails/matchers.rb
CHANGED
@@ -20,6 +20,7 @@ require 'rspec/rails/matchers/be_a_new'
|
|
20
20
|
require 'rspec/rails/matchers/relation_match_array'
|
21
21
|
require 'rspec/rails/matchers/be_valid'
|
22
22
|
require 'rspec/rails/matchers/have_http_status'
|
23
|
+
require 'rspec/rails/matchers/send_email'
|
23
24
|
|
24
25
|
if RSpec::Rails::FeatureCheck.has_active_job?
|
25
26
|
require 'rspec/rails/matchers/active_job'
|
@@ -12,9 +12,7 @@ if defined?(Capybara)
|
|
12
12
|
RSpec.configure do |c|
|
13
13
|
if defined?(Capybara::DSL)
|
14
14
|
c.include Capybara::DSL, type: :feature
|
15
|
-
|
16
|
-
c.include Capybara::DSL, type: :system
|
17
|
-
end
|
15
|
+
c.include Capybara::DSL, type: :system
|
18
16
|
end
|
19
17
|
|
20
18
|
if defined?(Capybara::RSpecMatchers)
|
data/lib/rspec/rails/version.rb
CHANGED
@@ -13,26 +13,8 @@ module RSpec
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# Compat-shim for AbstractController::Rendering#view_assigns
|
16
|
-
#
|
17
|
-
# _assigns was deprecated in favor of view_assigns after
|
18
|
-
# Rails-3.0.0 was released. Since we are not able to predict when
|
19
|
-
# the _assigns/view_assigns patch will be released (I thought it
|
20
|
-
# would have been in 3.0.1, but 3.0.1 bypassed this change for a
|
21
|
-
# security fix), this bit ensures that we do the right thing without
|
22
|
-
# knowing anything about the Rails version we are dealing with.
|
23
|
-
#
|
24
|
-
# Once that change _is_ released, this can be changed to something
|
25
|
-
# that checks for the Rails version when the module is being
|
26
|
-
# interpreted, as it was before commit dd0095.
|
27
16
|
def view_assigns
|
28
17
|
super.merge(_encapsulated_assigns)
|
29
|
-
rescue
|
30
|
-
_assigns
|
31
|
-
end
|
32
|
-
|
33
|
-
# @private
|
34
|
-
def _assigns
|
35
|
-
super.merge(_encapsulated_assigns)
|
36
18
|
end
|
37
19
|
|
38
20
|
private
|
@@ -62,14 +62,8 @@ module RSpec
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
template.format
|
68
|
-
end
|
69
|
-
else
|
70
|
-
def self.template_format(template)
|
71
|
-
template.formats
|
72
|
-
end
|
65
|
+
def self.template_format(template)
|
66
|
+
template.format
|
73
67
|
end
|
74
68
|
|
75
69
|
# Delegates all methods to the submitted resolver and for all methods
|
@@ -77,7 +71,15 @@ module RSpec
|
|
77
71
|
# templates with modified source
|
78
72
|
#
|
79
73
|
# @private
|
80
|
-
class ResolverDecorator
|
74
|
+
class ResolverDecorator < ::ActionView::Resolver
|
75
|
+
(::ActionView::Resolver.instance_methods - Object.instance_methods).each do |method|
|
76
|
+
undef_method method
|
77
|
+
end
|
78
|
+
|
79
|
+
(::ActionView::Resolver.methods - Object.methods).each do |method|
|
80
|
+
singleton_class.undef_method method
|
81
|
+
end
|
82
|
+
|
81
83
|
def initialize(resolver)
|
82
84
|
@resolver = resolver
|
83
85
|
end
|
@@ -125,11 +127,11 @@ module RSpec
|
|
125
127
|
# @private
|
126
128
|
module EmptyTemplates
|
127
129
|
def prepend_view_path(new_path)
|
128
|
-
|
130
|
+
super(_path_decorator(*new_path))
|
129
131
|
end
|
130
132
|
|
131
133
|
def append_view_path(new_path)
|
132
|
-
|
134
|
+
super(_path_decorator(*new_path))
|
133
135
|
end
|
134
136
|
|
135
137
|
private
|
data/lib/rspec-rails.rb
CHANGED
@@ -8,11 +8,24 @@ module RSpec
|
|
8
8
|
class Railtie < ::Rails::Railtie
|
9
9
|
# As of Rails 5.1.0 you can register directories to work with `rake notes`
|
10
10
|
require 'rails/source_annotation_extractor'
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
::Rails::SourceAnnotationExtractor::Annotation.register_directories("spec")
|
12
|
+
|
13
|
+
# As of Rails 8.0.0 you can register directories to work with `rails stats`
|
14
|
+
if ::Rails::VERSION::STRING >= "8.0.0"
|
15
|
+
require 'rails/code_statistics'
|
16
|
+
|
17
|
+
dirs = Dir['./spec/**/*_spec.rb']
|
18
|
+
.map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') }
|
19
|
+
.uniq
|
20
|
+
.select { |f| File.directory?(f) }
|
21
|
+
|
22
|
+
Hash[dirs.map { |d| [d.split('/').last, d] }].each do |type, dir|
|
23
|
+
name = type.singularize.capitalize
|
24
|
+
|
25
|
+
::Rails::CodeStatistics.register_directory "#{name} specs", dir, test_directory: true
|
26
|
+
end
|
15
27
|
end
|
28
|
+
|
16
29
|
generators = config.app_generators
|
17
30
|
generators.integration_tool :rspec
|
18
31
|
generators.test_framework :rspec
|
@@ -51,10 +64,18 @@ module RSpec
|
|
51
64
|
end
|
52
65
|
end
|
53
66
|
|
54
|
-
|
55
|
-
|
67
|
+
if ::Rails::VERSION::STRING >= "7.1.0"
|
68
|
+
def config_default_preview_path(options)
|
69
|
+
return unless options.preview_paths.empty?
|
56
70
|
|
57
|
-
|
71
|
+
options.preview_paths << "#{::Rails.root}/spec/mailers/previews"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
def config_default_preview_path(options)
|
75
|
+
return unless options.preview_path.blank?
|
76
|
+
|
77
|
+
options.preview_path = "#{::Rails.root}/spec/mailers/previews"
|
78
|
+
end
|
58
79
|
end
|
59
80
|
|
60
81
|
def supports_action_mailer_previews?(config)
|
data.tar.gz.sig
CHANGED
Binary file
|