##// END OF EJS Templates
Adds Message-Id and References headers to email notifications so that issues and messages threads can be displayed by email clients (#1401)....
Jean-Philippe Lang -
r2279:1d783106a34b
parent child
Show More
@@ -1,253 +1,297
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Mailer < ActionMailer::Base
19 19 helper :application
20 20 helper :issues
21 21 helper :custom_fields
22 22
23 23 include ActionController::UrlWriter
24 24
25 25 def issue_add(issue)
26 26 redmine_headers 'Project' => issue.project.identifier,
27 27 'Issue-Id' => issue.id,
28 28 'Issue-Author' => issue.author.login
29 29 redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
30 message_id issue
30 31 recipients issue.recipients
31 32 cc(issue.watcher_recipients - @recipients)
32 33 subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
33 34 body :issue => issue,
34 35 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
35 36 end
36 37
37 38 def issue_edit(journal)
38 39 issue = journal.journalized
39 40 redmine_headers 'Project' => issue.project.identifier,
40 41 'Issue-Id' => issue.id,
41 42 'Issue-Author' => issue.author.login
42 43 redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
44 message_id journal
45 references issue
43 46 @author = journal.user
44 47 recipients issue.recipients
45 48 # Watchers in cc
46 49 cc(issue.watcher_recipients - @recipients)
47 50 s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
48 51 s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
49 52 s << issue.subject
50 53 subject s
51 54 body :issue => issue,
52 55 :journal => journal,
53 56 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
54 57 end
55 58
56 59 def reminder(user, issues, days)
57 60 set_language_if_valid user.language
58 61 recipients user.mail
59 62 subject l(:mail_subject_reminder, issues.size)
60 63 body :issues => issues,
61 64 :days => days,
62 65 :issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'due_date', :sort_order => 'asc')
63 66 end
64 67
65 68 def document_added(document)
66 69 redmine_headers 'Project' => document.project.identifier
67 70 recipients document.project.recipients
68 71 subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
69 72 body :document => document,
70 73 :document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
71 74 end
72 75
73 76 def attachments_added(attachments)
74 77 container = attachments.first.container
75 78 added_to = ''
76 79 added_to_url = ''
77 80 case container.class.name
78 81 when 'Project'
79 82 added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container)
80 83 added_to = "#{l(:label_project)}: #{container}"
81 84 when 'Version'
82 85 added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
83 86 added_to = "#{l(:label_version)}: #{container.name}"
84 87 when 'Document'
85 88 added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
86 89 added_to = "#{l(:label_document)}: #{container.title}"
87 90 end
88 91 redmine_headers 'Project' => container.project.identifier
89 92 recipients container.project.recipients
90 93 subject "[#{container.project.name}] #{l(:label_attachment_new)}"
91 94 body :attachments => attachments,
92 95 :added_to => added_to,
93 96 :added_to_url => added_to_url
94 97 end
95 98
96 99 def news_added(news)
97 100 redmine_headers 'Project' => news.project.identifier
101 message_id news
98 102 recipients news.project.recipients
99 103 subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
100 104 body :news => news,
101 105 :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
102 106 end
103 107
104 108 def message_posted(message, recipients)
105 109 redmine_headers 'Project' => message.project.identifier,
106 110 'Topic-Id' => (message.parent_id || message.id)
111 message_id message
112 references message.parent unless message.parent.nil?
107 113 recipients(recipients)
108 114 subject "[#{message.board.project.name} - #{message.board.name}] #{message.subject}"
109 115 body :message => message,
110 116 :message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
111 117 end
112 118
113 119 def account_information(user, password)
114 120 set_language_if_valid user.language
115 121 recipients user.mail
116 122 subject l(:mail_subject_register, Setting.app_title)
117 123 body :user => user,
118 124 :password => password,
119 125 :login_url => url_for(:controller => 'account', :action => 'login')
120 126 end
121 127
122 128 def account_activation_request(user)
123 129 # Send the email to all active administrators
124 130 recipients User.active.find(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
125 131 subject l(:mail_subject_account_activation_request, Setting.app_title)
126 132 body :user => user,
127 133 :url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
128 134 end
129 135
130 136 def lost_password(token)
131 137 set_language_if_valid(token.user.language)
132 138 recipients token.user.mail
133 139 subject l(:mail_subject_lost_password, Setting.app_title)
134 140 body :token => token,
135 141 :url => url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
136 142 end
137 143
138 144 def register(token)
139 145 set_language_if_valid(token.user.language)
140 146 recipients token.user.mail
141 147 subject l(:mail_subject_register, Setting.app_title)
142 148 body :token => token,
143 149 :url => url_for(:controller => 'account', :action => 'activate', :token => token.value)
144 150 end
145 151
146 152 def test(user)
147 153 set_language_if_valid(user.language)
148 154 recipients user.mail
149 155 subject 'Redmine test'
150 156 body :url => url_for(:controller => 'welcome')
151 157 end
152 158
153 159 # Overrides default deliver! method to prevent from sending an email
154 160 # with no recipient, cc or bcc
155 161 def deliver!(mail = @mail)
156 162 return false if (recipients.nil? || recipients.empty?) &&
157 163 (cc.nil? || cc.empty?) &&
158 164 (bcc.nil? || bcc.empty?)
159 super
165
166 # Set Message-Id and References
167 if @message_id_object
168 mail.message_id = self.class.message_id_for(@message_id_object)
169 end
170 if @references_objects
171 mail.references = @references_objects.collect {|o| self.class.message_id_for(o)}
172 end
173 super(mail)
160 174 end
161 175
162 176 # Sends reminders to issue assignees
163 177 # Available options:
164 178 # * :days => how many days in the future to remind about (defaults to 7)
165 179 # * :tracker => id of tracker for filtering issues (defaults to all trackers)
166 180 # * :project => id or identifier of project to process (defaults to all projects)
167 181 def self.reminders(options={})
168 182 days = options[:days] || 7
169 183 project = options[:project] ? Project.find(options[:project]) : nil
170 184 tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
171 185
172 186 s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, days.day.from_now.to_date]
173 187 s << "#{Issue.table_name}.assigned_to_id IS NOT NULL"
174 188 s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}"
175 189 s << "#{Issue.table_name}.project_id = #{project.id}" if project
176 190 s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
177 191
178 192 issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker],
179 193 :conditions => s.conditions
180 194 ).group_by(&:assigned_to)
181 195 issues_by_assignee.each do |assignee, issues|
182 196 deliver_reminder(assignee, issues, days) unless assignee.nil?
183 197 end
184 198 end
185 199
186 200 private
187 201 def initialize_defaults(method_name)
188 202 super
189 203 set_language_if_valid Setting.default_language
190 204 from Setting.mail_from
191 205
192 206 # URL options
193 207 h = Setting.host_name
194 208 h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank?
195 209 default_url_options[:host] = h
196 210 default_url_options[:protocol] = Setting.protocol
197 211
198 212 # Common headers
199 213 headers 'X-Mailer' => 'Redmine',
200 214 'X-Redmine-Host' => Setting.host_name,
201 215 'X-Redmine-Site' => Setting.app_title
202 216 end
203 217
204 218 # Appends a Redmine header field (name is prepended with 'X-Redmine-')
205 219 def redmine_headers(h)
206 220 h.each { |k,v| headers["X-Redmine-#{k}"] = v }
207 221 end
208 222
209 223 # Overrides the create_mail method
210 224 def create_mail
211 225 # Removes the current user from the recipients and cc
212 226 # if he doesn't want to receive notifications about what he does
213 227 @author ||= User.current
214 228 if @author.pref[:no_self_notified]
215 229 recipients.delete(@author.mail) if recipients
216 230 cc.delete(@author.mail) if cc
217 231 end
218 232 # Blind carbon copy recipients
219 233 if Setting.bcc_recipients?
220 234 bcc([recipients, cc].flatten.compact.uniq)
221 235 recipients []
222 236 cc []
223 237 end
224 238 super
225 239 end
226 240
227 241 # Renders a message with the corresponding layout
228 242 def render_message(method_name, body)
229 243 layout = method_name.to_s.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
230 244 body[:content_for_layout] = render(:file => method_name, :body => body)
231 245 ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}", :use_full_path => true)
232 246 end
233 247
234 248 # for the case of plain text only
235 249 def body(*params)
236 250 value = super(*params)
237 251 if Setting.plain_text_mail?
238 252 templates = Dir.glob("#{template_path}/#{@template}.text.plain.{rhtml,erb}")
239 253 unless String === @body or templates.empty?
240 254 template = File.basename(templates.first)
241 255 @body[:content_for_layout] = render(:file => template, :body => @body)
242 256 @body = ActionView::Base.new(template_root, @body, self).render(:file => "mailer/layout.text.plain.rhtml", :use_full_path => true)
243 257 return @body
244 258 end
245 259 end
246 260 return value
247 261 end
248 262
249 263 # Makes partial rendering work with Rails 1.2 (retro-compatibility)
250 264 def self.controller_path
251 265 ''
252 266 end unless respond_to?('controller_path')
267
268 # Returns a predictable Message-Id for the given object
269 def self.message_id_for(object)
270 # id + timestamp should reduce the odds of a collision
271 # as far as we don't send multiple emails for the same object
272 hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{object.created_on.strftime("%Y%m%d%H%M%S")}"
273 host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
274 host = "#{::Socket.gethostname}.redmine" if host.empty?
275 "<#{hash}@#{host}>"
276 end
277
278 private
279
280 def message_id(object)
281 @message_id_object = object
282 end
283
284 def references(object)
285 @references_objects ||= []
286 @references_objects << object
287 end
288 end
289
290 # Patch TMail so that message_id is not overwritten
291 module TMail
292 class Mail
293 def add_message_id( fqdn = nil )
294 self.message_id ||= ::TMail::new_message_id(fqdn)
295 end
296 end
253 297 end
@@ -1,68 +1,68
1 1 ---
2 2 messages_001:
3 3 created_on: 2007-05-12 17:15:32 +02:00
4 4 updated_on: 2007-05-12 17:15:32 +02:00
5 5 subject: First post
6 6 id: 1
7 7 replies_count: 2
8 8 last_reply_id: 3
9 9 content: "This is the very first post\n\
10 10 in the forum"
11 11 author_id: 1
12 12 parent_id:
13 13 board_id: 1
14 14 messages_002:
15 15 created_on: 2007-05-12 17:18:00 +02:00
16 16 updated_on: 2007-05-12 17:18:00 +02:00
17 17 subject: First reply
18 18 id: 2
19 19 replies_count: 0
20 20 last_reply_id:
21 21 content: "Reply to the first post"
22 22 author_id: 1
23 23 parent_id: 1
24 24 board_id: 1
25 25 messages_003:
26 26 created_on: 2007-05-12 17:18:02 +02:00
27 27 updated_on: 2007-05-12 17:18:02 +02:00
28 28 subject: "RE: First post"
29 29 id: 3
30 30 replies_count: 0
31 31 last_reply_id:
32 32 content: "An other reply"
33 author_id:
33 author_id: 2
34 34 parent_id: 1
35 35 board_id: 1
36 36 messages_004:
37 37 created_on: 2007-08-12 17:15:32 +02:00
38 38 updated_on: 2007-08-12 17:15:32 +02:00
39 39 subject: Post 2
40 40 id: 4
41 41 replies_count: 2
42 42 last_reply_id: 6
43 43 content: "This is an other post"
44 44 author_id:
45 45 parent_id:
46 46 board_id: 1
47 47 messages_005:
48 48 created_on: <%= 3.days.ago.to_date.to_s(:db) %>
49 49 updated_on: <%= 3.days.ago.to_date.to_s(:db) %>
50 50 subject: 'RE: post 2'
51 51 id: 5
52 52 replies_count: 0
53 53 last_reply_id:
54 54 content: "Reply to the second post"
55 55 author_id: 1
56 56 parent_id: 4
57 57 board_id: 1
58 58 messages_006:
59 59 created_on: <%= 2.days.ago.to_date.to_s(:db) %>
60 60 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
61 61 subject: 'RE: post 2'
62 62 id: 6
63 63 replies_count: 0
64 64 last_reply_id:
65 65 content: "Another reply to the second post"
66 66 author_id: 3
67 67 parent_id: 4
68 68 board_id: 1
@@ -1,186 +1,224
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class MailerTest < Test::Unit::TestCase
21 21 fixtures :projects, :issues, :users, :members, :documents, :attachments, :news, :tokens, :journals, :journal_details, :changesets, :trackers, :issue_statuses, :enumerations, :messages, :boards, :repositories
22 22
23 23 def test_generated_links_in_emails
24 24 ActionMailer::Base.deliveries.clear
25 25 Setting.host_name = 'mydomain.foo'
26 26 Setting.protocol = 'https'
27 27
28 28 journal = Journal.find(2)
29 29 assert Mailer.deliver_issue_edit(journal)
30 30
31 31 mail = ActionMailer::Base.deliveries.last
32 32 assert_kind_of TMail::Mail, mail
33 33 # link to the main ticket
34 34 assert mail.body.include?('<a href="https://mydomain.foo/issues/show/1">Bug #1: Can\'t print recipes</a>')
35 35
36 36 # link to a referenced ticket
37 37 assert mail.body.include?('<a href="https://mydomain.foo/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
38 38 # link to a changeset
39 39 assert mail.body.include?('<a href="https://mydomain.foo/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
40 40 end
41 41
42 42 def test_generated_links_with_prefix
43 43 relative_url_root = Redmine::Utils.relative_url_root
44 44 ActionMailer::Base.deliveries.clear
45 45 Setting.host_name = 'mydomain.foo/rdm'
46 46 Setting.protocol = 'http'
47 47 Redmine::Utils.relative_url_root = '/rdm'
48 48
49 49 journal = Journal.find(2)
50 50 assert Mailer.deliver_issue_edit(journal)
51 51
52 52 mail = ActionMailer::Base.deliveries.last
53 53 assert_kind_of TMail::Mail, mail
54 54 # link to the main ticket
55 55 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/1">Bug #1: Can\'t print recipes</a>')
56 56
57 57 # link to a referenced ticket
58 58 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
59 59 # link to a changeset
60 60 assert mail.body.include?('<a href="http://mydomain.foo/rdm/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
61 61 ensure
62 62 # restore it
63 63 Redmine::Utils.relative_url_root = relative_url_root
64 64 end
65 65
66 66 def test_generated_links_with_prefix_and_no_relative_url_root
67 67 relative_url_root = Redmine::Utils.relative_url_root
68 68 ActionMailer::Base.deliveries.clear
69 69 Setting.host_name = 'mydomain.foo/rdm'
70 70 Setting.protocol = 'http'
71 71 Redmine::Utils.relative_url_root = nil
72 72
73 73 journal = Journal.find(2)
74 74 assert Mailer.deliver_issue_edit(journal)
75 75
76 76 mail = ActionMailer::Base.deliveries.last
77 77 assert_kind_of TMail::Mail, mail
78 78 # link to the main ticket
79 79 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/1">Bug #1: Can\'t print recipes</a>')
80 80
81 81 # link to a referenced ticket
82 82 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
83 83 # link to a changeset
84 84 assert mail.body.include?('<a href="http://mydomain.foo/rdm/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
85 85 ensure
86 86 # restore it
87 87 Redmine::Utils.relative_url_root = relative_url_root
88 88 end
89 89
90 90 def test_plain_text_mail
91 91 Setting.plain_text_mail = 1
92 92 journal = Journal.find(2)
93 93 Mailer.deliver_issue_edit(journal)
94 94 mail = ActionMailer::Base.deliveries.last
95 95 assert !mail.body.include?('<a href="https://mydomain.foo/issues/show/1">Bug #1: Can\'t print recipes</a>')
96 96 end
97 97
98 def test_issue_add_message_id
99 ActionMailer::Base.deliveries.clear
100 issue = Issue.find(1)
101 Mailer.deliver_issue_add(issue)
102 mail = ActionMailer::Base.deliveries.last
103 assert_not_nil mail
104 assert_equal Mailer.message_id_for(issue), mail.message_id
105 assert_nil mail.references
106 end
98 107
108 def test_issue_edit_message_id
109 ActionMailer::Base.deliveries.clear
110 journal = Journal.find(1)
111 Mailer.deliver_issue_edit(journal)
112 mail = ActionMailer::Base.deliveries.last
113 assert_not_nil mail
114 assert_equal Mailer.message_id_for(journal), mail.message_id
115 assert_equal Mailer.message_id_for(journal.issue), mail.references.to_s
116 end
117
118 def test_message_posted_message_id
119 ActionMailer::Base.deliveries.clear
120 message = Message.find(1)
121 Mailer.deliver_message_posted(message, message.author.mail)
122 mail = ActionMailer::Base.deliveries.last
123 assert_not_nil mail
124 assert_equal Mailer.message_id_for(message), mail.message_id
125 assert_nil mail.references
126 end
127
128 def test_reply_posted_message_id
129 ActionMailer::Base.deliveries.clear
130 message = Message.find(3)
131 Mailer.deliver_message_posted(message, message.author.mail)
132 mail = ActionMailer::Base.deliveries.last
133 assert_not_nil mail
134 assert_equal Mailer.message_id_for(message), mail.message_id
135 assert_equal Mailer.message_id_for(message.parent), mail.references.to_s
136 end
99 137
100 138 # test mailer methods for each language
101 139 def test_issue_add
102 140 issue = Issue.find(1)
103 141 GLoc.valid_languages.each do |lang|
104 142 Setting.default_language = lang.to_s
105 143 assert Mailer.deliver_issue_add(issue)
106 144 end
107 145 end
108 146
109 147 def test_issue_edit
110 148 journal = Journal.find(1)
111 149 GLoc.valid_languages.each do |lang|
112 150 Setting.default_language = lang.to_s
113 151 assert Mailer.deliver_issue_edit(journal)
114 152 end
115 153 end
116 154
117 155 def test_document_added
118 156 document = Document.find(1)
119 157 GLoc.valid_languages.each do |lang|
120 158 Setting.default_language = lang.to_s
121 159 assert Mailer.deliver_document_added(document)
122 160 end
123 161 end
124 162
125 163 def test_attachments_added
126 164 attachements = [ Attachment.find_by_container_type('Document') ]
127 165 GLoc.valid_languages.each do |lang|
128 166 Setting.default_language = lang.to_s
129 167 assert Mailer.deliver_attachments_added(attachements)
130 168 end
131 169 end
132 170
133 171 def test_news_added
134 172 news = News.find(:first)
135 173 GLoc.valid_languages.each do |lang|
136 174 Setting.default_language = lang.to_s
137 175 assert Mailer.deliver_news_added(news)
138 176 end
139 177 end
140 178
141 179 def test_message_posted
142 180 message = Message.find(:first)
143 181 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
144 182 recipients = recipients.compact.uniq
145 183 GLoc.valid_languages.each do |lang|
146 184 Setting.default_language = lang.to_s
147 185 assert Mailer.deliver_message_posted(message, recipients)
148 186 end
149 187 end
150 188
151 189 def test_account_information
152 190 user = User.find(:first)
153 191 GLoc.valid_languages.each do |lang|
154 192 user.update_attribute :language, lang.to_s
155 193 user.reload
156 194 assert Mailer.deliver_account_information(user, 'pAsswORd')
157 195 end
158 196 end
159 197
160 198 def test_lost_password
161 199 token = Token.find(2)
162 200 GLoc.valid_languages.each do |lang|
163 201 token.user.update_attribute :language, lang.to_s
164 202 token.reload
165 203 assert Mailer.deliver_lost_password(token)
166 204 end
167 205 end
168 206
169 207 def test_register
170 208 token = Token.find(1)
171 209 GLoc.valid_languages.each do |lang|
172 210 token.user.update_attribute :language, lang.to_s
173 211 token.reload
174 212 assert Mailer.deliver_register(token)
175 213 end
176 214 end
177 215
178 216 def test_reminders
179 217 ActionMailer::Base.deliveries.clear
180 218 Mailer.reminders(:days => 42)
181 219 assert_equal 1, ActionMailer::Base.deliveries.size
182 220 mail = ActionMailer::Base.deliveries.last
183 221 assert mail.bcc.include?('dlopper@somenet.foo')
184 222 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
185 223 end
186 224 end
General Comments 0
You need to be logged in to leave comments. Login now