aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer/lib/action_mailer/test_helper.rb
blob: b3403e8331a7160f3991c989e65d6d302927dc90 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# frozen_string_literal: true

require "active_job"

module ActionMailer
  # Provides helper methods for testing Action Mailer, including #assert_emails
  # and #assert_no_emails.
  module TestHelper
    include ActiveJob::TestHelper

    # Asserts that the number of emails sent matches the given number.
    #
    #   def test_emails
    #     assert_emails 0
    #     ContactMailer.welcome.deliver_now
    #     assert_emails 1
    #     ContactMailer.welcome.deliver_now
    #     assert_emails 2
    #   end
    #
    # If a block is passed, that block should cause the specified number of
    # emails to be sent.
    #
    #   def test_emails_again
    #     assert_emails 1 do
    #       ContactMailer.welcome.deliver_now
    #     end
    #
    #     assert_emails 2 do
    #       ContactMailer.welcome.deliver_now
    #       ContactMailer.welcome.deliver_later
    #     end
    #   end
    def assert_emails(number, &block)
      if block_given?
        original_count = ActionMailer::Base.deliveries.size
        perform_enqueued_jobs(only: ->(job) { delivery_job_filter(job) }, &block)
        new_count = ActionMailer::Base.deliveries.size
        assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent"
      else
        assert_equal number, ActionMailer::Base.deliveries.size
      end
    end

    # Asserts that no emails have been sent.
    #
    #   def test_emails
    #     assert_no_emails
    #     ContactMailer.welcome.deliver_now
    #     assert_emails 1
    #   end
    #
    # If a block is passed, that block should not cause any emails to be sent.
    #
    #   def test_emails_again
    #     assert_no_emails do
    #       # No emails should be sent from this block
    #     end
    #   end
    #
    # Note: This assertion is simply a shortcut for:
    #
    #   assert_emails 0, &block
    def assert_no_emails(&block)
      assert_emails 0, &block
    end

    # Asserts that the number of emails enqueued for later delivery matches
    # the given number.
    #
    #   def test_emails
    #     assert_enqueued_emails 0
    #     ContactMailer.welcome.deliver_later
    #     assert_enqueued_emails 1
    #     ContactMailer.welcome.deliver_later
    #     assert_enqueued_emails 2
    #   end
    #
    # If a block is passed, that block should cause the specified number of
    # emails to be enqueued.
    #
    #   def test_emails_again
    #     assert_enqueued_emails 1 do
    #       ContactMailer.welcome.deliver_later
    #     end
    #
    #     assert_enqueued_emails 2 do
    #       ContactMailer.welcome.deliver_later
    #       ContactMailer.welcome.deliver_later
    #     end
    #   end
    def assert_enqueued_emails(number, &block)
      assert_enqueued_jobs(number, only: ->(job) { delivery_job_filter(job) }, &block)
    end

    # Asserts that a specific email has been enqueued, optionally
    # matching arguments.
    #
    #   def test_email
    #     ContactMailer.welcome.deliver_later
    #     assert_enqueued_email_with ContactMailer, :welcome
    #   end
    #
    #   def test_email_with_arguments
    #     ContactMailer.welcome("Hello", "Goodbye").deliver_later
    #     assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
    #   end
    #
    # If a block is passed, that block should cause the specified email
    # to be enqueued.
    #
    #   def test_email_in_block
    #     assert_enqueued_email_with ContactMailer, :welcome do
    #       ContactMailer.welcome.deliver_later
    #     end
    #   end
    #
    # If +args+ is provided as a Hash, a parameterized email is matched.
    #
    #   def test_parameterized_email
    #     assert_enqueued_email_with ContactMailer, :welcome,
    #       args: {email: 'user@example.com'} do
    #       ContactMailer.with(email: 'user@example.com').welcome.deliver_later
    #     end
    #   end
    def assert_enqueued_email_with(mailer, method, args: nil, queue: "mailers", &block)
      if args.is_a? Hash
        job = mailer.parameterized_delivery_job
        args = [mailer.to_s, method.to_s, "deliver_now", args]
      else
        job = mailer.delivery_job
        args = [mailer.to_s, method.to_s, "deliver_now", *args]
      end

      assert_enqueued_with(job: job, args: args, queue: queue, &block)
    end

    # Asserts that no emails are enqueued for later delivery.
    #
    #   def test_no_emails
    #     assert_no_enqueued_emails
    #     ContactMailer.welcome.deliver_later
    #     assert_enqueued_emails 1
    #   end
    #
    # If a block is provided, it should not cause any emails to be enqueued.
    #
    #   def test_no_emails
    #     assert_no_enqueued_emails do
    #       # No emails should be enqueued from this block
    #     end
    #   end
    def assert_no_enqueued_emails(&block)
      assert_enqueued_emails 0, &block
    end

    private

      def delivery_job_filter(job)
        job_class = job.is_a?(Hash) ? job.fetch(:job) : job.class

        Base.descendants.map(&:delivery_job).include?(job_class) ||
        Base.descendants.map(&:parameterized_delivery_job).include?(job_class)
      end
  end
end