rspec-rails 3.9.0 → 4.0.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/Capybara.md +5 -54
- data/Changelog.md +156 -78
- data/README.md +8 -7
- data/lib/generators/rspec/channel/channel_generator.rb +12 -0
- data/lib/generators/rspec/{observer/templates/observer_spec.rb → channel/templates/channel_spec.rb.erb} +1 -1
- data/lib/generators/rspec/controller/controller_generator.rb +21 -4
- data/lib/generators/rspec/controller/templates/request_spec.rb +14 -0
- data/lib/generators/rspec/controller/templates/routing_spec.rb +13 -0
- data/lib/generators/rspec/feature/feature_generator.rb +2 -2
- data/lib/generators/rspec/{generators → generator}/generator_generator.rb +2 -2
- data/lib/generators/rspec/{generators → generator}/templates/generator_spec.rb +0 -0
- data/lib/generators/rspec/helper/helper_generator.rb +1 -1
- data/lib/generators/rspec/install/install_generator.rb +4 -4
- data/lib/generators/rspec/install/templates/spec/rails_helper.rb +17 -16
- data/lib/generators/rspec/integration/integration_generator.rb +3 -3
- data/lib/generators/rspec/mailbox/mailbox_generator.rb +14 -0
- data/lib/generators/rspec/mailbox/templates/mailbox_spec.rb.erb +7 -0
- data/lib/generators/rspec/mailer/mailer_generator.rb +2 -1
- data/lib/generators/rspec/model/model_generator.rb +5 -4
- data/lib/generators/rspec/model/templates/fixtures.yml +1 -1
- data/lib/generators/rspec/request/request_generator.rb +1 -1
- data/lib/generators/rspec/scaffold/scaffold_generator.rb +29 -19
- data/lib/generators/rspec/scaffold/templates/api_controller_spec.rb +0 -36
- data/lib/generators/rspec/scaffold/templates/api_request_spec.rb +131 -0
- data/lib/generators/rspec/scaffold/templates/controller_spec.rb +10 -10
- data/lib/generators/rspec/scaffold/templates/edit_spec.rb +1 -1
- data/lib/generators/rspec/scaffold/templates/index_spec.rb +2 -2
- data/lib/generators/rspec/scaffold/templates/new_spec.rb +1 -1
- data/lib/generators/rspec/scaffold/templates/request_spec.rb +133 -0
- data/lib/generators/rspec/scaffold/templates/routing_spec.rb +8 -10
- 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 +0 -6
- data/lib/rspec/rails/adapters.rb +11 -76
- data/lib/rspec/rails/configuration.rb +43 -33
- data/lib/rspec/rails/example/channel_example_group.rb +93 -0
- data/lib/rspec/rails/example/controller_example_group.rb +4 -4
- data/lib/rspec/rails/example/feature_example_group.rb +6 -26
- data/lib/rspec/rails/example/helper_example_group.rb +2 -9
- data/lib/rspec/rails/example/mailbox_example_group.rb +80 -0
- data/lib/rspec/rails/example/mailer_example_group.rb +2 -2
- data/lib/rspec/rails/example/rails_example_group.rb +1 -1
- data/lib/rspec/rails/example/system_example_group.rb +26 -10
- data/lib/rspec/rails/example/view_example_group.rb +38 -27
- data/lib/rspec/rails/example.rb +2 -0
- data/lib/rspec/rails/extensions/active_record/proxy.rb +1 -9
- data/lib/rspec/rails/feature_check.rb +12 -29
- data/lib/rspec/rails/fixture_file_upload_support.rb +1 -1
- data/lib/rspec/rails/fixture_support.rb +37 -31
- data/lib/rspec/rails/matchers/action_cable/have_broadcasted_to.rb +170 -0
- data/lib/rspec/rails/matchers/action_cable/have_streams.rb +58 -0
- data/lib/rspec/rails/matchers/action_cable.rb +65 -0
- data/lib/rspec/rails/matchers/action_mailbox.rb +64 -0
- data/lib/rspec/rails/matchers/active_job.rb +148 -22
- data/lib/rspec/rails/matchers/base_matcher.rb +5 -10
- data/lib/rspec/rails/matchers/have_enqueued_mail.rb +52 -28
- data/lib/rspec/rails/matchers/have_http_status.rb +11 -7
- data/lib/rspec/rails/matchers/have_rendered.rb +1 -0
- data/lib/rspec/rails/matchers/routing_matchers.rb +12 -12
- data/lib/rspec/rails/matchers.rb +10 -0
- data/lib/rspec/rails/tasks/rspec.rake +7 -17
- data/lib/rspec/rails/vendor/capybara.rb +10 -15
- data/lib/rspec/rails/version.rb +1 -1
- data/lib/rspec/rails/view_path_builder.rb +1 -1
- data/lib/rspec/rails/view_rendering.rb +15 -4
- data/lib/rspec-rails.rb +8 -9
- data.tar.gz.sig +0 -0
- metadata +46 -34
- metadata.gz.sig +0 -0
- data/lib/generators/rspec/observer/observer_generator.rb +0 -13
@@ -8,14 +8,14 @@ module RSpec
|
|
8
8
|
#
|
9
9
|
# @api private
|
10
10
|
module ActiveJob
|
11
|
-
# rubocop: disable
|
11
|
+
# rubocop: disable Metrics/ClassLength
|
12
12
|
# @private
|
13
13
|
class Base < RSpec::Rails::Matchers::BaseMatcher
|
14
14
|
def initialize
|
15
15
|
@args = []
|
16
16
|
@queue = nil
|
17
17
|
@at = nil
|
18
|
-
@block =
|
18
|
+
@block = proc { }
|
19
19
|
set_expected_number(:exactly, 1)
|
20
20
|
end
|
21
21
|
|
@@ -26,12 +26,16 @@ module RSpec
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def on_queue(queue)
|
29
|
-
@queue = queue
|
29
|
+
@queue = queue.to_s
|
30
30
|
self
|
31
31
|
end
|
32
32
|
|
33
|
-
def at(
|
34
|
-
|
33
|
+
def at(time_or_date)
|
34
|
+
case time_or_date
|
35
|
+
when Time then @at = Time.at(time_or_date.to_f)
|
36
|
+
else
|
37
|
+
@at = time_or_date
|
38
|
+
end
|
35
39
|
self
|
36
40
|
end
|
37
41
|
|
@@ -67,7 +71,7 @@ module RSpec
|
|
67
71
|
end
|
68
72
|
|
69
73
|
def failure_message
|
70
|
-
"expected to
|
74
|
+
"expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
|
71
75
|
if @unmatching_jobs.any?
|
72
76
|
msg << "\nQueued jobs:"
|
73
77
|
@unmatching_jobs.each do |job|
|
@@ -78,7 +82,7 @@ module RSpec
|
|
78
82
|
end
|
79
83
|
|
80
84
|
def failure_message_when_negated
|
81
|
-
"expected not to
|
85
|
+
"expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
|
82
86
|
end
|
83
87
|
|
84
88
|
def message_expectation_modifier
|
@@ -97,7 +101,7 @@ module RSpec
|
|
97
101
|
|
98
102
|
def check(jobs)
|
99
103
|
@matching_jobs, @unmatching_jobs = jobs.partition do |job|
|
100
|
-
if arguments_match?(job) &&
|
104
|
+
if job_match?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job)
|
101
105
|
args = deserialize_arguments(job)
|
102
106
|
@block.call(*args)
|
103
107
|
true
|
@@ -119,7 +123,7 @@ module RSpec
|
|
119
123
|
msg << " with #{@args}," if @args.any?
|
120
124
|
msg << " on queue #{@queue}," if @queue
|
121
125
|
msg << " at #{@at.inspect}," if @at
|
122
|
-
msg << " but
|
126
|
+
msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
|
123
127
|
end
|
124
128
|
end
|
125
129
|
|
@@ -134,29 +138,32 @@ module RSpec
|
|
134
138
|
end
|
135
139
|
end
|
136
140
|
|
141
|
+
def job_match?(job)
|
142
|
+
@job ? @job == job[:job] : true
|
143
|
+
end
|
144
|
+
|
137
145
|
def arguments_match?(job)
|
138
146
|
if @args.any?
|
147
|
+
args = serialize_and_deserialize_arguments(@args)
|
139
148
|
deserialized_args = deserialize_arguments(job)
|
140
|
-
RSpec::Mocks::ArgumentListMatcher.new(
|
149
|
+
RSpec::Mocks::ArgumentListMatcher.new(*args).args_match?(*deserialized_args)
|
141
150
|
else
|
142
151
|
true
|
143
152
|
end
|
144
153
|
end
|
145
154
|
|
146
|
-
def
|
147
|
-
|
148
|
-
end
|
155
|
+
def queue_match?(job)
|
156
|
+
return true unless @queue
|
149
157
|
|
150
|
-
|
151
|
-
{}.tap do |attributes|
|
152
|
-
attributes[:at] = serialized_at if @at
|
153
|
-
attributes[:queue] = @queue if @queue
|
154
|
-
attributes[:job] = @job if @job
|
155
|
-
end
|
158
|
+
@queue == job[:queue]
|
156
159
|
end
|
157
160
|
|
158
|
-
def
|
159
|
-
|
161
|
+
def at_match?(job)
|
162
|
+
return true unless @at
|
163
|
+
return job[:at].nil? if @at == :no_wait
|
164
|
+
return false unless job[:at]
|
165
|
+
|
166
|
+
values_match?(@at, Time.at(job[:at]))
|
160
167
|
end
|
161
168
|
|
162
169
|
def set_expected_number(relativity, count)
|
@@ -169,6 +176,13 @@ module RSpec
|
|
169
176
|
end
|
170
177
|
end
|
171
178
|
|
179
|
+
def serialize_and_deserialize_arguments(args)
|
180
|
+
serialized = ::ActiveJob::Arguments.serialize(args)
|
181
|
+
::ActiveJob::Arguments.deserialize(serialized)
|
182
|
+
rescue ::ActiveJob::SerializationError
|
183
|
+
args
|
184
|
+
end
|
185
|
+
|
172
186
|
def deserialize_arguments(job)
|
173
187
|
::ActiveJob::Arguments.deserialize(job[:args])
|
174
188
|
rescue ::ActiveJob::DeserializationError
|
@@ -179,10 +193,13 @@ module RSpec
|
|
179
193
|
::ActiveJob::Base.queue_adapter
|
180
194
|
end
|
181
195
|
end
|
182
|
-
# rubocop: enable
|
196
|
+
# rubocop: enable Metrics/ClassLength
|
183
197
|
|
184
198
|
# @private
|
185
199
|
class HaveEnqueuedJob < Base
|
200
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
|
201
|
+
MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
|
202
|
+
|
186
203
|
def initialize(job)
|
187
204
|
super()
|
188
205
|
@job = job
|
@@ -207,6 +224,9 @@ module RSpec
|
|
207
224
|
|
208
225
|
# @private
|
209
226
|
class HaveBeenEnqueued < Base
|
227
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
|
228
|
+
MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
|
229
|
+
|
210
230
|
def matches?(job)
|
211
231
|
@job = job
|
212
232
|
check(queue_adapter.enqueued_jobs)
|
@@ -218,6 +238,38 @@ module RSpec
|
|
218
238
|
!matches?(proc)
|
219
239
|
end
|
220
240
|
end
|
241
|
+
|
242
|
+
# @private
|
243
|
+
class HavePerformedJob < Base
|
244
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
|
245
|
+
MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
|
246
|
+
|
247
|
+
def initialize(job)
|
248
|
+
super()
|
249
|
+
@job = job
|
250
|
+
end
|
251
|
+
|
252
|
+
def matches?(proc)
|
253
|
+
raise ArgumentError, "have_performed_job only supports block expectations" unless Proc === proc
|
254
|
+
|
255
|
+
original_performed_jobs_count = queue_adapter.performed_jobs.count
|
256
|
+
proc.call
|
257
|
+
in_block_jobs = queue_adapter.performed_jobs.drop(original_performed_jobs_count)
|
258
|
+
|
259
|
+
check(in_block_jobs)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# @private
|
264
|
+
class HaveBeenPerformed < Base
|
265
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
|
266
|
+
MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
|
267
|
+
|
268
|
+
def matches?(job)
|
269
|
+
@job = job
|
270
|
+
check(queue_adapter.performed_jobs)
|
271
|
+
end
|
272
|
+
end
|
221
273
|
end
|
222
274
|
|
223
275
|
# @api public
|
@@ -305,11 +357,85 @@ module RSpec
|
|
305
357
|
ActiveJob::HaveBeenEnqueued.new
|
306
358
|
end
|
307
359
|
|
360
|
+
# @api public
|
361
|
+
# Passes if a job has been performed inside block. May chain at_least, at_most or exactly to specify a number of times.
|
362
|
+
#
|
363
|
+
# @example
|
364
|
+
# expect {
|
365
|
+
# perform_jobs { HeavyLiftingJob.perform_later }
|
366
|
+
# }.to have_performed_job
|
367
|
+
#
|
368
|
+
# expect {
|
369
|
+
# perform_jobs {
|
370
|
+
# HelloJob.perform_later
|
371
|
+
# HeavyLiftingJob.perform_later
|
372
|
+
# }
|
373
|
+
# }.to have_performed_job(HelloJob).exactly(:once)
|
374
|
+
#
|
375
|
+
# expect {
|
376
|
+
# perform_jobs { 3.times { HelloJob.perform_later } }
|
377
|
+
# }.to have_performed_job(HelloJob).at_least(2).times
|
378
|
+
#
|
379
|
+
# expect {
|
380
|
+
# perform_jobs { HelloJob.perform_later }
|
381
|
+
# }.to have_performed_job(HelloJob).at_most(:twice)
|
382
|
+
#
|
383
|
+
# expect {
|
384
|
+
# perform_jobs {
|
385
|
+
# HelloJob.perform_later
|
386
|
+
# HeavyLiftingJob.perform_later
|
387
|
+
# }
|
388
|
+
# }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
|
389
|
+
#
|
390
|
+
# expect {
|
391
|
+
# perform_jobs {
|
392
|
+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
393
|
+
# }
|
394
|
+
# }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
|
395
|
+
def have_performed_job(job = nil)
|
396
|
+
check_active_job_adapter
|
397
|
+
ActiveJob::HavePerformedJob.new(job)
|
398
|
+
end
|
399
|
+
alias_method :perform_job, :have_performed_job
|
400
|
+
|
401
|
+
# @api public
|
402
|
+
# Passes if a job has been performed. May chain at_least, at_most or exactly to specify a number of times.
|
403
|
+
#
|
404
|
+
# @example
|
405
|
+
# before do
|
406
|
+
# ActiveJob::Base.queue_adapter.performed_jobs.clear
|
407
|
+
# ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
|
408
|
+
# ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
|
409
|
+
# end
|
410
|
+
#
|
411
|
+
# HeavyLiftingJob.perform_later
|
412
|
+
# expect(HeavyLiftingJob).to have_been_performed
|
413
|
+
#
|
414
|
+
# HelloJob.perform_later
|
415
|
+
# HeavyLiftingJob.perform_later
|
416
|
+
# expect(HeavyLiftingJob).to have_been_performed.exactly(:once)
|
417
|
+
#
|
418
|
+
# 3.times { HelloJob.perform_later }
|
419
|
+
# expect(HelloJob).to have_been_performed.at_least(2).times
|
420
|
+
#
|
421
|
+
# HelloJob.perform_later
|
422
|
+
# HeavyLiftingJob.perform_later
|
423
|
+
# expect(HelloJob).to have_been_performed
|
424
|
+
# expect(HeavyLiftingJob).to have_been_performed
|
425
|
+
#
|
426
|
+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
427
|
+
# expect(HelloJob).to have_been_performed.with(42).on_queue("low").at(Date.tomorrow.noon)
|
428
|
+
def have_been_performed
|
429
|
+
check_active_job_adapter
|
430
|
+
ActiveJob::HaveBeenPerformed.new
|
431
|
+
end
|
432
|
+
|
308
433
|
private
|
309
434
|
|
310
435
|
# @private
|
311
436
|
def check_active_job_adapter
|
312
437
|
return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
|
438
|
+
|
313
439
|
raise StandardError, "To use ActiveJob matchers set `ActiveJob::Base.queue_adapter = :test`"
|
314
440
|
end
|
315
441
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Rails
|
3
3
|
module Matchers
|
4
|
-
# @
|
4
|
+
# @api private
|
5
|
+
#
|
6
|
+
# Base class to build matchers. Should not be instantiated directly.
|
5
7
|
class BaseMatcher
|
6
8
|
include RSpec::Matchers::Composable
|
7
9
|
|
@@ -114,19 +116,12 @@ module RSpec
|
|
114
116
|
|
115
117
|
def assert_ivars(*expected_ivars)
|
116
118
|
return unless (expected_ivars - present_ivars).any?
|
119
|
+
|
117
120
|
ivar_list = RSpec::Matchers::EnglishPhrasing.list(expected_ivars)
|
118
121
|
raise "#{self.class.name} needs to supply#{ivar_list}"
|
119
122
|
end
|
120
123
|
|
121
|
-
|
122
|
-
# :nocov:
|
123
|
-
def present_ivars
|
124
|
-
instance_variables.map(&:to_sym)
|
125
|
-
end
|
126
|
-
# :nocov:
|
127
|
-
else
|
128
|
-
alias present_ivars instance_variables
|
129
|
-
end
|
124
|
+
alias present_ivars instance_variables
|
130
125
|
|
131
126
|
# @private
|
132
127
|
module HashFormatting
|
@@ -1,4 +1,7 @@
|
|
1
|
-
require
|
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"
|
2
5
|
require "rspec/rails/matchers/active_job"
|
3
6
|
|
4
7
|
module RSpec
|
@@ -6,33 +9,32 @@ module RSpec
|
|
6
9
|
module Matchers
|
7
10
|
# Matcher class for `have_enqueued_mail`. Should not be instantiated directly.
|
8
11
|
#
|
9
|
-
# rubocop: disable Style/ClassLength
|
10
12
|
# @private
|
11
13
|
# @see RSpec::Rails::Matchers#have_enqueued_mail
|
12
14
|
class HaveEnqueuedMail < ActiveJob::HaveEnqueuedJob
|
13
15
|
MAILER_JOB_METHOD = 'deliver_now'.freeze
|
14
16
|
|
15
|
-
include RSpec::Mocks::
|
17
|
+
include RSpec::Mocks::ArgumentMatchers
|
16
18
|
|
17
19
|
def initialize(mailer_class, method_name)
|
18
|
-
super(
|
20
|
+
super(nil)
|
19
21
|
@mailer_class = mailer_class
|
20
22
|
@method_name = method_name
|
21
23
|
@mail_args = []
|
22
|
-
@args = mailer_args
|
23
24
|
end
|
24
25
|
|
25
26
|
def description
|
26
|
-
"enqueues #{
|
27
|
+
"enqueues #{mailer_class_name}.#{@method_name}"
|
27
28
|
end
|
28
29
|
|
29
30
|
def with(*args, &block)
|
30
31
|
@mail_args = args
|
31
|
-
block.nil? ? super
|
32
|
+
block.nil? ? super : super(&yield_mail_args(block))
|
32
33
|
end
|
33
34
|
|
34
35
|
def matches?(block)
|
35
36
|
raise ArgumentError, 'have_enqueued_mail and enqueue_mail only work with block arguments' unless block.respond_to?(:call)
|
37
|
+
|
36
38
|
check_active_job_adapter
|
37
39
|
super
|
38
40
|
end
|
@@ -50,7 +52,7 @@ module RSpec
|
|
50
52
|
private
|
51
53
|
|
52
54
|
def base_message
|
53
|
-
|
55
|
+
[mailer_class_name, @method_name].compact.join('.').tap do |msg|
|
54
56
|
msg << " #{expected_count_message}"
|
55
57
|
msg << " with #{@mail_args}," if @mail_args.any?
|
56
58
|
msg << " on queue #{@queue}," if @queue
|
@@ -63,38 +65,46 @@ module RSpec
|
|
63
65
|
"#{message_expectation_modifier} #{@expected_number} #{@expected_number == 1 ? 'time' : 'times'}"
|
64
66
|
end
|
65
67
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
else
|
70
|
-
mailer_method_arity = @mailer_class.instance_method(@method_name).arity
|
68
|
+
def mailer_class_name
|
69
|
+
@mailer_class ? @mailer_class.name : 'ActionMailer::Base'
|
70
|
+
end
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
mailer_method_arity
|
76
|
-
end
|
72
|
+
def job_match?(job)
|
73
|
+
legacy_mail?(job) || parameterized_mail?(job) || unified_mail?(job)
|
74
|
+
end
|
77
75
|
|
78
|
-
|
79
|
-
|
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)
|
80
89
|
end
|
81
90
|
|
82
91
|
def base_mailer_args
|
83
|
-
[
|
92
|
+
[mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
|
84
93
|
end
|
85
94
|
|
86
95
|
def yield_mail_args(block)
|
87
|
-
|
96
|
+
proc { |*job_args| block.call(*(job_args - base_mailer_args)) }
|
88
97
|
end
|
89
98
|
|
90
99
|
def check_active_job_adapter
|
91
100
|
return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
|
101
|
+
|
92
102
|
raise StandardError, "To use HaveEnqueuedMail matcher set `ActiveJob::Base.queue_adapter = :test`"
|
93
103
|
end
|
94
104
|
|
95
105
|
def unmatching_mail_jobs
|
96
106
|
@unmatching_jobs.select do |job|
|
97
|
-
job
|
107
|
+
job_match?(job)
|
98
108
|
end
|
99
109
|
end
|
100
110
|
|
@@ -120,12 +130,18 @@ module RSpec
|
|
120
130
|
"#{mailer_method} #{msg_parts.join(', ')}".strip
|
121
131
|
end
|
122
132
|
|
123
|
-
def
|
124
|
-
ActionMailer::DeliveryJob
|
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
|
125
139
|
end
|
126
|
-
end
|
127
|
-
# rubocop: enable Style/ClassLength
|
128
140
|
|
141
|
+
def unified_mail?(job)
|
142
|
+
RSpec::Rails::FeatureCheck.has_action_mailer_unified_delivery? && job[:job] <= ActionMailer::MailDeliveryJob
|
143
|
+
end
|
144
|
+
end
|
129
145
|
# @api public
|
130
146
|
# Passes if an email has been enqueued inside block.
|
131
147
|
# May chain with to specify expected arguments.
|
@@ -136,6 +152,14 @@ module RSpec
|
|
136
152
|
# @example
|
137
153
|
# expect {
|
138
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
|
139
163
|
# }.to have_enqueued_mail(MyMailer, :welcome)
|
140
164
|
#
|
141
165
|
# # Using alias
|
@@ -163,7 +187,7 @@ module RSpec
|
|
163
187
|
# expect {
|
164
188
|
# MyMailer.welcome(user).deliver_later(queue: :urgent_mail)
|
165
189
|
# }.to have_enqueued_mail(MyMailer, :welcome).on_queue(:urgent_mail)
|
166
|
-
def have_enqueued_mail(mailer_class, mail_method_name)
|
190
|
+
def have_enqueued_mail(mailer_class = nil, mail_method_name = nil)
|
167
191
|
HaveEnqueuedMail.new(mailer_class, mail_method_name)
|
168
192
|
end
|
169
193
|
alias_method :have_enqueued_email, :have_enqueued_mail
|
@@ -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,7 +73,7 @@ module RSpec
|
|
72
73
|
# @example
|
73
74
|
# expect(response).to have_http_status(404)
|
74
75
|
#
|
75
|
-
# @see RSpec::Rails::Matchers
|
76
|
+
# @see RSpec::Rails::Matchers#have_http_status
|
76
77
|
class NumericCode < RSpec::Rails::Matchers::BaseMatcher
|
77
78
|
include HaveHttpStatus
|
78
79
|
|
@@ -122,7 +123,7 @@ module RSpec
|
|
122
123
|
# @example
|
123
124
|
# expect(response).to have_http_status(:created)
|
124
125
|
#
|
125
|
-
# @see RSpec::Rails::Matchers
|
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
128
|
class SymbolicStatus < RSpec::Rails::Matchers::BaseMatcher
|
128
129
|
include HaveHttpStatus
|
@@ -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,8 +236,8 @@ 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
|
238
|
-
# @see ActionDispatch::TestResponse
|
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`
|
239
241
|
class GenericStatus < RSpec::Rails::Matchers::BaseMatcher
|
240
242
|
include HaveHttpStatus
|
241
243
|
|
@@ -254,6 +256,7 @@ module RSpec
|
|
254
256
|
unless self.class.valid_statuses.include?(type)
|
255
257
|
raise ArgumentError, "Invalid generic HTTP status: #{type.inspect}"
|
256
258
|
end
|
259
|
+
|
257
260
|
@expected = type
|
258
261
|
@actual = nil
|
259
262
|
@invalid_response = nil
|
@@ -290,9 +293,9 @@ module RSpec
|
|
290
293
|
protected
|
291
294
|
|
292
295
|
RESPONSE_METHODS = {
|
293
|
-
:
|
294
|
-
:
|
295
|
-
:
|
296
|
+
success: 'successful',
|
297
|
+
error: 'server_error',
|
298
|
+
missing: 'not_found'
|
296
299
|
}.freeze
|
297
300
|
|
298
301
|
def check_expected_status(test_response, expected)
|
@@ -374,6 +377,7 @@ module RSpec
|
|
374
377
|
# @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
|
375
378
|
def have_http_status(target)
|
376
379
|
raise ArgumentError, "Invalid HTTP status: nil" unless target
|
380
|
+
|
377
381
|
HaveHttpStatus.matcher_for_status(target)
|
378
382
|
end
|
379
383
|
end
|
@@ -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!(:
|
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
|
-
{
|
29
|
+
{method: verb_to_path_map.keys.first, path: path},
|
30
30
|
Rack::Utils.parse_nested_query(query)
|
31
31
|
)
|
32
32
|
end
|
@@ -50,14 +50,14 @@ module RSpec
|
|
50
50
|
#
|
51
51
|
# @example
|
52
52
|
#
|
53
|
-
# expect(:
|
54
|
-
# :
|
55
|
-
# :
|
53
|
+
# expect(get: "/things/special").to route_to(
|
54
|
+
# controller: "things",
|
55
|
+
# action: "special"
|
56
56
|
# )
|
57
57
|
#
|
58
|
-
# expect(:
|
58
|
+
# expect(get: "/things/special").to route_to("things#special")
|
59
59
|
#
|
60
|
-
# @see
|
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
|
@@ -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, :
|
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(:
|
99
|
-
# expect(:
|
100
|
-
# expect(:
|
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
|
-
{
|
118
|
+
{method.to_sym => path}
|
119
119
|
end
|
120
120
|
end
|
121
121
|
end
|
data/lib/rspec/rails/matchers.rb
CHANGED
@@ -20,6 +20,16 @@ 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
|
+
|
23
24
|
if RSpec::Rails::FeatureCheck.has_active_job?
|
24
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'
|
25
35
|
end
|
@@ -3,19 +3,19 @@ if default = Rake.application.instance_variable_get('@tasks')['default']
|
|
3
3
|
default.prerequisites.delete('test')
|
4
4
|
end
|
5
5
|
|
6
|
-
task :
|
6
|
+
task default: :spec
|
7
7
|
|
8
|
-
task :
|
8
|
+
task stats: "spec:statsetup"
|
9
9
|
|
10
10
|
desc "Run all specs in spec directory (excluding plugin specs)"
|
11
|
-
RSpec::Core::RakeTask.new(:
|
11
|
+
RSpec::Core::RakeTask.new(spec: "spec:prepare")
|
12
12
|
|
13
13
|
namespace :spec do
|
14
14
|
types = begin
|
15
|
-
dirs = Dir['./spec/**/*_spec.rb']
|
16
|
-
map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') }
|
17
|
-
uniq
|
18
|
-
select { |f| File.directory?(f) }
|
15
|
+
dirs = Dir['./spec/**/*_spec.rb']
|
16
|
+
.map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') }
|
17
|
+
.uniq
|
18
|
+
.select { |f| File.directory?(f) }
|
19
19
|
Hash[dirs.map { |d| [d.split('/').last, d] }]
|
20
20
|
end
|
21
21
|
|
@@ -35,16 +35,6 @@ namespace :spec do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
# RCov task only enabled for Ruby 1.8
|
39
|
-
if RUBY_VERSION < '1.9'
|
40
|
-
desc "Run all specs with rcov"
|
41
|
-
RSpec::Core::RakeTask.new(:rcov => "spec:prepare") do |t|
|
42
|
-
t.rcov = true
|
43
|
-
t.pattern = "./spec/**/*_spec.rb"
|
44
|
-
t.rcov_opts = '--exclude /gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec-,spec'
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
38
|
task :statsetup do
|
49
39
|
require 'rails/code_statistics'
|
50
40
|
types.each do |type, dir|
|