Skip to content

Commit 3cc2457

Browse files
petergoldsteinfabnpirj
committed
Fix arguments for have_enqueued_mail matcher
Add additional logic to the ActionMailer argument parsing to accomodate for differences under Ruby 3.1/Rails 6.1 Co-authored-by: Fabio Napoleoni <f.napoleoni@gmail.com> Co-authored-by: Phil Pirozhkov <hello@fili.pp.ru>
1 parent b49cfc1 commit 3cc2457

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

.rubocop_todo.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ Layout/LineLength:
99
# Over time we'd like to get this down, but this is what we're at now.
1010
Metrics/MethodLength:
1111
Max: 43 # default: 10
12+
13+
Metrics/ClassLength:
14+
Exclude:
15+
- lib/rspec/rails/matchers/have_enqueued_mail.rb

lib/rspec/rails/feature_check.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ def has_action_mailbox?
3939
defined?(::ActionMailbox)
4040
end
4141

42+
def ruby_3_1?
43+
RUBY_VERSION >= "3.1"
44+
end
45+
4246
def type_metatag(type)
4347
"type: :#{type}"
4448
end

lib/rspec/rails/matchers/have_enqueued_mail.rb

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def job_match?(job)
7676
def arguments_match?(job)
7777
@args =
7878
if @mail_args.any?
79-
base_mailer_args + @mail_args
79+
base_mailer_args + process_arguments(job, @mail_args)
8080
elsif @mailer_class && @method_name
8181
base_mailer_args + [any_args]
8282
elsif @mailer_class
@@ -88,12 +88,38 @@ def arguments_match?(job)
8888
super(job)
8989
end
9090

91+
def process_arguments(job, given_mail_args)
92+
# Old matcher behavior working with all builtin classes but ActionMailer::MailDeliveryJob
93+
return given_mail_args if use_given_mail_args?(job)
94+
95+
# If matching args starts with a hash and job instance has params match with them
96+
if given_mail_args.first.is_a?(Hash) && job[:args][3]['params'].present?
97+
[hash_including(params: given_mail_args[0], args: given_mail_args.drop(1))]
98+
else
99+
[hash_including(args: given_mail_args)]
100+
end
101+
end
102+
103+
def use_given_mail_args?(job)
104+
return true if FeatureCheck.has_action_mailer_parameterized? && job[:job] <= ActionMailer::Parameterized::DeliveryJob
105+
return false if FeatureCheck.ruby_3_1?
106+
107+
!(FeatureCheck.has_action_mailer_unified_delivery? && job[:job] <= ActionMailer::MailDeliveryJob)
108+
end
109+
91110
def base_mailer_args
92111
[mailer_class_name, @method_name.to_s, MAILER_JOB_METHOD]
93112
end
94113

95114
def yield_mail_args(block)
96-
proc { |*job_args| block.call(*(job_args - base_mailer_args)) }
115+
proc do |*job_args|
116+
mailer_args = job_args - base_mailer_args
117+
if mailer_args.first.is_a?(Hash)
118+
block.call(*mailer_args.first[:args])
119+
else
120+
block.call(*mailer_args)
121+
end
122+
end
97123
end
98124

99125
def check_active_job_adapter
@@ -120,16 +146,23 @@ def unmatching_mail_jobs_message
120146

121147
def mail_job_message(job)
122148
mailer_method = job[:args][0..1].join('.')
123-
124-
mailer_args = job[:args][3..-1]
149+
mailer_args = deserialize_arguments(job)[3..-1]
150+
mailer_args = mailer_args.first[:args] if unified_mail?(job)
125151
msg_parts = []
126-
msg_parts << "with #{mailer_args}" if mailer_args.any?
152+
display_args = display_mailer_args(mailer_args)
153+
msg_parts << "with #{display_args}" if display_args.any?
127154
msg_parts << "on queue #{job[:queue]}" if job[:queue] && job[:queue] != 'mailers'
128155
msg_parts << "at #{Time.at(job[:at])}" if job[:at]
129156

130157
"#{mailer_method} #{msg_parts.join(', ')}".strip
131158
end
132159

160+
def display_mailer_args(mailer_args)
161+
return mailer_args unless mailer_args.first.is_a?(Hash) && mailer_args.first.key?(:args)
162+
163+
mailer_args.first[:args]
164+
end
165+
133166
def legacy_mail?(job)
134167
job[:job] <= ActionMailer::DeliveryJob
135168
end

spec/rspec/rails/matchers/have_enqueued_mail_spec.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -393,18 +393,22 @@ def self.name; "NonMailerJob"; end
393393
}.to have_enqueued_mail(UnifiedMailer, :test_email).and have_enqueued_mail(UnifiedMailer, :email_with_args)
394394
end
395395

396-
it "passes with provided argument matchers" do
396+
it "matches arguments when mailer has only args" do
397+
expect {
398+
UnifiedMailer.email_with_args(1, 2).deliver_later
399+
}.to have_enqueued_mail(UnifiedMailer, :email_with_args).with(1, 2)
400+
end
401+
402+
it "matches arguments when mailer is parameterized" do
397403
expect {
398404
UnifiedMailer.with('foo' => 'bar').test_email.deliver_later
399-
}.to have_enqueued_mail(UnifiedMailer, :test_email).with(
400-
a_hash_including(params: {'foo' => 'bar'})
401-
)
405+
}.to have_enqueued_mail(UnifiedMailer, :test_email).with('foo' => 'bar')
406+
end
402407

408+
it "matches arguments when mixing parameterized and non-parameterized emails" do
403409
expect {
404410
UnifiedMailer.with('foo' => 'bar').email_with_args(1, 2).deliver_later
405-
}.to have_enqueued_mail(UnifiedMailer, :email_with_args).with(
406-
a_hash_including(params: {'foo' => 'bar'}, args: [1, 2])
407-
)
411+
}.to have_enqueued_mail(UnifiedMailer, :email_with_args).with({'foo' => 'bar'}, 1, 2)
408412
end
409413

410414
it "passes when using a mailer with `delivery_job` set to a sub class of `ActionMailer::DeliveryJob`" do

0 commit comments

Comments
 (0)