rspec-rails 3.9.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Capybara.md +5 -54
- data/Changelog.md +167 -80
- data/README.md +4 -5
- 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 +1 -1
- data/lib/rspec/rails/example/rails_example_group.rb +1 -1
- data/lib/rspec/rails/example/system_example_group.rb +24 -8
- 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 +142 -20
- 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 +9 -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 +42 -30
- 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,7 +26,7 @@ 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
|
|
@@ -67,7 +67,7 @@ module RSpec
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def failure_message
|
70
|
-
"expected to
|
70
|
+
"expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
|
71
71
|
if @unmatching_jobs.any?
|
72
72
|
msg << "\nQueued jobs:"
|
73
73
|
@unmatching_jobs.each do |job|
|
@@ -78,7 +78,7 @@ module RSpec
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def failure_message_when_negated
|
81
|
-
"expected not to
|
81
|
+
"expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
|
82
82
|
end
|
83
83
|
|
84
84
|
def message_expectation_modifier
|
@@ -97,7 +97,7 @@ module RSpec
|
|
97
97
|
|
98
98
|
def check(jobs)
|
99
99
|
@matching_jobs, @unmatching_jobs = jobs.partition do |job|
|
100
|
-
if arguments_match?(job) &&
|
100
|
+
if job_match?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job)
|
101
101
|
args = deserialize_arguments(job)
|
102
102
|
@block.call(*args)
|
103
103
|
true
|
@@ -119,7 +119,7 @@ module RSpec
|
|
119
119
|
msg << " with #{@args}," if @args.any?
|
120
120
|
msg << " on queue #{@queue}," if @queue
|
121
121
|
msg << " at #{@at.inspect}," if @at
|
122
|
-
msg << " but
|
122
|
+
msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
@@ -134,29 +134,32 @@ module RSpec
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
def job_match?(job)
|
138
|
+
@job ? @job == job[:job] : true
|
139
|
+
end
|
140
|
+
|
137
141
|
def arguments_match?(job)
|
138
142
|
if @args.any?
|
143
|
+
args = serialize_and_deserialize_arguments(@args)
|
139
144
|
deserialized_args = deserialize_arguments(job)
|
140
|
-
RSpec::Mocks::ArgumentListMatcher.new(
|
145
|
+
RSpec::Mocks::ArgumentListMatcher.new(*args).args_match?(*deserialized_args)
|
141
146
|
else
|
142
147
|
true
|
143
148
|
end
|
144
149
|
end
|
145
150
|
|
146
|
-
def
|
147
|
-
|
148
|
-
end
|
151
|
+
def queue_match?(job)
|
152
|
+
return true unless @queue
|
149
153
|
|
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
|
154
|
+
@queue == job[:queue]
|
156
155
|
end
|
157
156
|
|
158
|
-
def
|
159
|
-
|
157
|
+
def at_match?(job)
|
158
|
+
return true unless @at
|
159
|
+
return job[:at].nil? if @at == :no_wait
|
160
|
+
return false unless job[:at]
|
161
|
+
|
162
|
+
values_match?(@at, Time.at(job[:at]))
|
160
163
|
end
|
161
164
|
|
162
165
|
def set_expected_number(relativity, count)
|
@@ -169,6 +172,13 @@ module RSpec
|
|
169
172
|
end
|
170
173
|
end
|
171
174
|
|
175
|
+
def serialize_and_deserialize_arguments(args)
|
176
|
+
serialized = ::ActiveJob::Arguments.serialize(args)
|
177
|
+
::ActiveJob::Arguments.deserialize(serialized)
|
178
|
+
rescue ::ActiveJob::SerializationError
|
179
|
+
args
|
180
|
+
end
|
181
|
+
|
172
182
|
def deserialize_arguments(job)
|
173
183
|
::ActiveJob::Arguments.deserialize(job[:args])
|
174
184
|
rescue ::ActiveJob::DeserializationError
|
@@ -179,10 +189,13 @@ module RSpec
|
|
179
189
|
::ActiveJob::Base.queue_adapter
|
180
190
|
end
|
181
191
|
end
|
182
|
-
# rubocop: enable
|
192
|
+
# rubocop: enable Metrics/ClassLength
|
183
193
|
|
184
194
|
# @private
|
185
195
|
class HaveEnqueuedJob < Base
|
196
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
|
197
|
+
MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
|
198
|
+
|
186
199
|
def initialize(job)
|
187
200
|
super()
|
188
201
|
@job = job
|
@@ -207,6 +220,9 @@ module RSpec
|
|
207
220
|
|
208
221
|
# @private
|
209
222
|
class HaveBeenEnqueued < Base
|
223
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'enqueue'.freeze
|
224
|
+
MESSAGE_EXPECTATION_ACTION = 'enqueued'.freeze
|
225
|
+
|
210
226
|
def matches?(job)
|
211
227
|
@job = job
|
212
228
|
check(queue_adapter.enqueued_jobs)
|
@@ -218,6 +234,38 @@ module RSpec
|
|
218
234
|
!matches?(proc)
|
219
235
|
end
|
220
236
|
end
|
237
|
+
|
238
|
+
# @private
|
239
|
+
class HavePerformedJob < Base
|
240
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
|
241
|
+
MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
|
242
|
+
|
243
|
+
def initialize(job)
|
244
|
+
super()
|
245
|
+
@job = job
|
246
|
+
end
|
247
|
+
|
248
|
+
def matches?(proc)
|
249
|
+
raise ArgumentError, "have_performed_job only supports block expectations" unless Proc === proc
|
250
|
+
|
251
|
+
original_performed_jobs_count = queue_adapter.performed_jobs.count
|
252
|
+
proc.call
|
253
|
+
in_block_jobs = queue_adapter.performed_jobs.drop(original_performed_jobs_count)
|
254
|
+
|
255
|
+
check(in_block_jobs)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# @private
|
260
|
+
class HaveBeenPerformed < Base
|
261
|
+
FAILURE_MESSAGE_EXPECTATION_ACTION = 'perform'.freeze
|
262
|
+
MESSAGE_EXPECTATION_ACTION = 'performed'.freeze
|
263
|
+
|
264
|
+
def matches?(job)
|
265
|
+
@job = job
|
266
|
+
check(queue_adapter.performed_jobs)
|
267
|
+
end
|
268
|
+
end
|
221
269
|
end
|
222
270
|
|
223
271
|
# @api public
|
@@ -305,11 +353,85 @@ module RSpec
|
|
305
353
|
ActiveJob::HaveBeenEnqueued.new
|
306
354
|
end
|
307
355
|
|
356
|
+
# @api public
|
357
|
+
# Passes if a job has been performed inside block. May chain at_least, at_most or exactly to specify a number of times.
|
358
|
+
#
|
359
|
+
# @example
|
360
|
+
# expect {
|
361
|
+
# perform_jobs { HeavyLiftingJob.perform_later }
|
362
|
+
# }.to have_performed_job
|
363
|
+
#
|
364
|
+
# expect {
|
365
|
+
# perform_jobs {
|
366
|
+
# HelloJob.perform_later
|
367
|
+
# HeavyLiftingJob.perform_later
|
368
|
+
# }
|
369
|
+
# }.to have_performed_job(HelloJob).exactly(:once)
|
370
|
+
#
|
371
|
+
# expect {
|
372
|
+
# perform_jobs { 3.times { HelloJob.perform_later } }
|
373
|
+
# }.to have_performed_job(HelloJob).at_least(2).times
|
374
|
+
#
|
375
|
+
# expect {
|
376
|
+
# perform_jobs { HelloJob.perform_later }
|
377
|
+
# }.to have_performed_job(HelloJob).at_most(:twice)
|
378
|
+
#
|
379
|
+
# expect {
|
380
|
+
# perform_jobs {
|
381
|
+
# HelloJob.perform_later
|
382
|
+
# HeavyLiftingJob.perform_later
|
383
|
+
# }
|
384
|
+
# }.to have_performed_job(HelloJob).and have_performed_job(HeavyLiftingJob)
|
385
|
+
#
|
386
|
+
# expect {
|
387
|
+
# perform_jobs {
|
388
|
+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
389
|
+
# }
|
390
|
+
# }.to have_performed_job.with(42).on_queue("low").at(Date.tomorrow.noon)
|
391
|
+
def have_performed_job(job = nil)
|
392
|
+
check_active_job_adapter
|
393
|
+
ActiveJob::HavePerformedJob.new(job)
|
394
|
+
end
|
395
|
+
alias_method :perform_job, :have_performed_job
|
396
|
+
|
397
|
+
# @api public
|
398
|
+
# Passes if a job has been performed. May chain at_least, at_most or exactly to specify a number of times.
|
399
|
+
#
|
400
|
+
# @example
|
401
|
+
# before do
|
402
|
+
# ActiveJob::Base.queue_adapter.performed_jobs.clear
|
403
|
+
# ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
|
404
|
+
# ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
|
405
|
+
# end
|
406
|
+
#
|
407
|
+
# HeavyLiftingJob.perform_later
|
408
|
+
# expect(HeavyLiftingJob).to have_been_performed
|
409
|
+
#
|
410
|
+
# HelloJob.perform_later
|
411
|
+
# HeavyLiftingJob.perform_later
|
412
|
+
# expect(HeavyLiftingJob).to have_been_performed.exactly(:once)
|
413
|
+
#
|
414
|
+
# 3.times { HelloJob.perform_later }
|
415
|
+
# expect(HelloJob).to have_been_performed.at_least(2).times
|
416
|
+
#
|
417
|
+
# HelloJob.perform_later
|
418
|
+
# HeavyLiftingJob.perform_later
|
419
|
+
# expect(HelloJob).to have_been_performed
|
420
|
+
# expect(HeavyLiftingJob).to have_been_performed
|
421
|
+
#
|
422
|
+
# HelloJob.set(wait_until: Date.tomorrow.noon, queue: "low").perform_later(42)
|
423
|
+
# expect(HelloJob).to have_been_performed.with(42).on_queue("low").at(Date.tomorrow.noon)
|
424
|
+
def have_been_performed
|
425
|
+
check_active_job_adapter
|
426
|
+
ActiveJob::HaveBeenPerformed.new
|
427
|
+
end
|
428
|
+
|
308
429
|
private
|
309
430
|
|
310
431
|
# @private
|
311
432
|
def check_active_job_adapter
|
312
433
|
return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
|
434
|
+
|
313
435
|
raise StandardError, "To use ActiveJob matchers set `ActiveJob::Base.queue_adapter = :test`"
|
314
436
|
end
|
315
437
|
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,7 +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'
|
25
26
|
require 'rspec/rails/matchers/have_enqueued_mail'
|
26
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'
|
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|
|