discourse/spec/mailers/user_notifications_spec.rb
Martin Brennan 6e2be3e60b
FIX: When admin changes an email for the user the user must confirm the change (#10830)
See https://meta.discourse.org/t/changing-a-users-email/164512 for additional context.

Previously when an admin user changed a user's email we assumed that they would need a password reset too because they likely did not have access to their account. This proved to be incorrect, as there are other reasons a user needs admin to change their email. This PR:

* Changes the admin change email for user flow so the user is sent an email to confirm the change
* We now record who the email change request was requested by
* If the requested by user is admin and not the user we note this in the email sent to the user
* We also make the confirm change email route open to anonymous users, so it can be clicked by the user even if they do not have access to their account. If there is a logged in user we make sure the confirmation matches the current user.
2020-10-07 13:02:24 +10:00

1024 lines
36 KiB
Ruby

# frozen_string_literal: true
require "rails_helper"
describe UserNotifications do
let(:user) { Fabricate(:admin) }
describe "#get_context_posts" do
it "does not include hidden/deleted/user_deleted posts in context" do
post1 = create_post
_post2 = Fabricate(:post, topic: post1.topic, deleted_at: 1.day.ago)
_post3 = Fabricate(:post, topic: post1.topic, user_deleted: true)
_post4 = Fabricate(:post, topic: post1.topic, hidden: true)
_post5 = Fabricate(:post, topic: post1.topic, post_type: Post.types[:moderator_action])
_post6 = Fabricate(:post, topic: post1.topic, post_type: Post.types[:small_action])
_post7 = Fabricate(:post, topic: post1.topic, post_type: Post.types[:whisper])
last = Fabricate(:post, topic: post1.topic)
post1.user.user_option.email_previous_replies = UserOption.previous_replies_type[:always]
# default is only post #1
expect(UserNotifications.get_context_posts(last, nil, post1.user).count).to eq(1)
# staff members can also see the whisper
moderator = build(:moderator)
moderator.user_option = UserOption.new
moderator.user_option.email_previous_replies = UserOption.previous_replies_type[:always]
tu = TopicUser.new(topic: post1.topic, user: moderator)
expect(UserNotifications.get_context_posts(last, tu, tu.user).count).to eq(2)
end
it "allows users to control context" do
post1 = create_post
_post2 = Fabricate(:post, topic: post1.topic)
post3 = Fabricate(:post, topic: post1.topic)
user = Fabricate(:user)
TopicUser.change(user.id, post1.topic_id, last_emailed_post_number: 1)
topic_user = TopicUser.find_by(user_id: user.id, topic_id: post1.topic_id)
# to avoid reloads after update_columns
user = topic_user.user
user.user_option.update_columns(email_previous_replies: UserOption.previous_replies_type[:unless_emailed])
expect(UserNotifications.get_context_posts(post3, topic_user, user).count).to eq(1)
user.user_option.update_columns(email_previous_replies: UserOption.previous_replies_type[:never])
expect(UserNotifications.get_context_posts(post3, topic_user, user).count).to eq(0)
user.user_option.update_columns(email_previous_replies: UserOption.previous_replies_type[:always])
expect(UserNotifications.get_context_posts(post3, topic_user, user).count).to eq(2)
SiteSetting.private_email = true
expect(UserNotifications.get_context_posts(post3, topic_user, user).count).to eq(0)
end
end
describe ".signup" do
subject { UserNotifications.signup(user) }
it "works" do
expect(subject.to).to eq([user.email])
expect(subject.subject).to be_present
expect(subject.from).to eq([SiteSetting.notification_email])
expect(subject.body).to be_present
end
end
describe ".forgot_password" do
subject { UserNotifications.forgot_password(user) }
it "works" do
expect(subject.to).to eq([user.email])
expect(subject.subject).to be_present
expect(subject.from).to eq([SiteSetting.notification_email])
expect(subject.body).to be_present
end
end
describe ".confirm_new_email" do
let(:opts) do
{ requested_by_admin: requested_by_admin, email_token: token }
end
let(:token) { "test123" }
context "when requested by admin" do
let(:requested_by_admin) { true }
it "uses the requested by admin template" do
expect(UserNotifications.confirm_new_email(user, opts).body).to include(
"This email change was requested by a site admin."
)
end
end
context "when not requested by admin" do
let(:requested_by_admin) { false }
it "uses the normal template" do
expect(UserNotifications.confirm_new_email(user, opts).body).not_to include(
"This email change was requested by a site admin."
)
end
end
end
describe '.email_login' do
let(:email_token) { user.email_tokens.create!(email: user.email).token }
subject { UserNotifications.email_login(user, email_token: email_token) }
it "generates the right email" do
expect(subject.to).to eq([user.email])
expect(subject.from).to eq([SiteSetting.notification_email])
expect(subject.subject).to eq(I18n.t(
'user_notifications.email_login.subject_template',
email_prefix: SiteSetting.title
))
expect(subject.body.to_s).to match(I18n.t(
'user_notifications.email_login.text_body_template',
site_name: SiteSetting.title,
base_url: Discourse.base_url,
email_token: email_token
))
end
end
describe '.digest' do
subject { UserNotifications.digest(user) }
after do
Discourse.redis.keys('summary-new-users:*').each { |key| Discourse.redis.del(key) }
end
context "without new topics" do
it "doesn't send the email" do
expect(subject.to).to be_blank
end
end
context "with topics only from new users" do
let!(:new_today) { Fabricate(:topic, user: Fabricate(:user, trust_level: TrustLevel[0], created_at: 10.minutes.ago), title: "Hey everyone look at me") }
let!(:new_yesterday) { Fabricate(:topic, user: Fabricate(:user, trust_level: TrustLevel[0], created_at: 25.hours.ago), created_at: 25.hours.ago, title: "This topic is of interest to you") }
it "returns topics from new users if they're more than 24 hours old" do
expect(subject.to).to eq([user.email])
html = subject.html_part.body.to_s
expect(html).to include(new_yesterday.title)
expect(html).to_not include(new_today.title)
end
end
context "with new topics" do
let!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) }
it "works" do
expect(subject.to).to eq([user.email])
expect(subject.subject).to be_present
expect(subject.from).to eq([SiteSetting.notification_email])
expect(subject.html_part.body.to_s).to be_present
expect(subject.text_part.body.to_s).to be_present
expect(subject.header["List-Unsubscribe"].to_s).to match(/\/email\/unsubscribe\/\h{64}/)
expect(subject.html_part.body.to_s).to include('New Users')
end
it "doesn't include new user count if digest_after_minutes is low" do
user.user_option.digest_after_minutes = 60
expect(subject.html_part.body.to_s).to_not include('New Users')
end
it "works with min_date string" do
digest = UserNotifications.digest(user, since: 1.month.ago.to_date.to_s)
expect(digest.html_part.body.to_s).to be_present
expect(digest.text_part.body.to_s).to be_present
expect(digest.html_part.body.to_s).to include('New Users')
end
it "includes email_prefix in email subject instead of site title" do
SiteSetting.email_prefix = "Try Discourse"
SiteSetting.title = "Discourse Meta"
expect(subject.subject).to match(/Try Discourse/)
expect(subject.subject).not_to match(/Discourse Meta/)
end
it "excludes deleted topics and their posts" do
deleted = Fabricate(:topic, user: Fabricate(:user), title: "Delete this topic plz", created_at: 1.hour.ago)
post = Fabricate(:post, topic: deleted, score: 100.0, post_number: 2, raw: "Your wish is my command", created_at: 1.hour.ago)
deleted.trash!
html = subject.html_part.body.to_s
expect(html).to_not include deleted.title
expect(html).to_not include post.raw
end
it "excludes shared drafts" do
cat = Fabricate(:category)
SiteSetting.shared_drafts_category = cat.id
topic = Fabricate(:topic, title: "This is a draft", category_id: cat.id, created_at: 1.hour.ago)
post = Fabricate(
:post,
topic: topic,
score: 100.0,
post_number: 2,
raw: "secret draft content",
created_at: 1.hour.ago
)
html = subject.html_part.body.to_s
expect(html).to_not include topic.title
expect(html).to_not include post.raw
end
it "excludes whispers and other post types that don't belong" do
t = Fabricate(:topic, user: Fabricate(:user), title: "Who likes the same stuff I like?", created_at: 1.hour.ago)
whisper = Fabricate(:post, topic: t, score: 100.0, post_number: 2, raw: "You like weird stuff", post_type: Post.types[:whisper], created_at: 1.hour.ago)
mod_action = Fabricate(:post, topic: t, score: 100.0, post_number: 3, raw: "This topic unlisted", post_type: Post.types[:moderator_action], created_at: 1.hour.ago)
small_action = Fabricate(:post, topic: t, score: 100.0, post_number: 4, raw: "A small action", post_type: Post.types[:small_action], created_at: 1.hour.ago)
html = subject.html_part.body.to_s
expect(html).to_not include whisper.raw
expect(html).to_not include mod_action.raw
expect(html).to_not include small_action.raw
end
it "excludes deleted and hidden posts" do
t = Fabricate(:topic, user: Fabricate(:user), title: "Post objectionable stuff here", created_at: 1.hour.ago)
deleted = Fabricate(:post, topic: t, score: 100.0, post_number: 2, raw: "This post is uncalled for", deleted_at: 5.minutes.ago, created_at: 1.hour.ago)
hidden = Fabricate(:post, topic: t, score: 100.0, post_number: 3, raw: "Try to find this post", hidden: true, hidden_at: 5.minutes.ago, hidden_reason_id: Post.hidden_reasons[:flagged_by_tl3_user], created_at: 1.hour.ago)
user_deleted = Fabricate(:post, topic: t, score: 100.0, post_number: 4, raw: "I regret this post", user_deleted: true, created_at: 1.hour.ago)
html = subject.html_part.body.to_s
expect(html).to_not include deleted.raw
expect(html).to_not include hidden.raw
expect(html).to_not include user_deleted.raw
end
it "excludes posts that are newer than editing grace period" do
SiteSetting.editing_grace_period = 5.minutes
too_new = Fabricate(:topic, user: Fabricate(:user), title: "Oops I need to edit this", created_at: 1.minute.ago)
_too_new_post = Fabricate(:post, user: too_new.user, topic: too_new, score: 100.0, post_number: 1, created_at: 1.minute.ago)
html = subject.html_part.body.to_s
expect(html).to_not include too_new.title
end
it "uses theme color" do
cs = Fabricate(:color_scheme, name: 'Fancy', color_scheme_colors: [
Fabricate(:color_scheme_color, name: 'header_primary', hex: 'F0F0F0'),
Fabricate(:color_scheme_color, name: 'header_background', hex: '1E1E1E')
])
theme = Fabricate(:theme,
user_selectable: true,
user: Fabricate(:admin),
color_scheme_id: cs.id
)
theme.set_default!
html = subject.html_part.body.to_s
expect(html).to include 'F0F0F0'
expect(html).to include '1E1E1E'
end
it "supports subfolder" do
set_subfolder "/forum"
html = subject.html_part.body.to_s
text = subject.text_part.body.to_s
expect(html).to be_present
expect(text).to be_present
expect(html).to_not include("/forum/forum")
expect(text).to_not include("/forum/forum")
expect(subject.header["List-Unsubscribe"].to_s).to match(/http:\/\/test.localhost\/forum\/email\/unsubscribe\/\h{64}/)
topic_url = "http://test.localhost/forum/t/#{popular_topic.slug}/#{popular_topic.id}"
expect(html).to include(topic_url)
expect(text).to include(topic_url)
end
it "applies lang/xml:lang html attributes" do
SiteSetting.default_locale = "pl_PL"
html = subject.html_part.to_s
expect(html).to match(' lang="pl-PL"')
expect(html).to match(' xml:lang="pl-PL"')
end
end
end
describe '.user_replied' do
let(:response_by_user) { Fabricate(:user, name: "John Doe") }
let(:category) { Fabricate(:category, name: 'India') }
let(:tag1) { Fabricate(:tag, name: 'Taggo') }
let(:tag2) { Fabricate(:tag, name: 'Taggie') }
let(:topic) { Fabricate(:topic, category: category, tags: [tag1, tag2], title: "Super cool topic") }
let(:post) { Fabricate(:post, topic: topic, raw: 'This is My super duper cool topic') }
let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) }
let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:replied_notification, user: user, post: response) }
it 'generates a correct email' do
SiteSetting.default_email_in_reply_to = true
# Fabricator is not fabricating this ...
SiteSetting.email_subject = "[%{site_name}] %{optional_pm}%{optional_cat}%{optional_tags}%{topic_title}"
SiteSetting.enable_names = true
SiteSetting.display_name_on_posts = true
mail = UserNotifications.user_replied(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
# from should include full user name
expect(mail[:from].display_names).to eql(['John Doe via Discourse'])
# subject should include category name
expect(mail.subject).to match(/India/)
# subject should include tag names
expect(mail.subject).to match(/Taggo/)
expect(mail.subject).to match(/Taggie/)
mail_html = mail.html_part.body.to_s
expect(mail_html.scan(/My super duper cool topic/).count).to eq(1)
expect(mail_html.scan(/In Reply To/).count).to eq(1)
# 2 "visit topic" link
expect(mail_html.scan(/Visit Topic/).count).to eq(2)
# 2 respond to links cause we have 1 context post
expect(mail_html.scan(/to respond/).count).to eq(2)
# 1 unsubscribe
expect(mail_html.scan(/To unsubscribe/).count).to eq(1)
# side effect, topic user is updated with post number
tu = TopicUser.get(post.topic_id, user)
expect(tu.last_emailed_post_number).to eq(response.post_number)
# no In Reply To if user opts out
user.user_option.email_in_reply_to = false
mail = UserNotifications.user_replied(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.html_part.body.to_s.scan(/In Reply To/).count).to eq(0)
SiteSetting.enable_names = true
SiteSetting.display_name_on_posts = true
SiteSetting.prioritize_username_in_ux = false
response.user.username = "bobmarley"
response.user.name = "Bob Marley"
response.user.save
mail = UserNotifications.user_replied(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
mail_html = mail.html_part.body.to_s
expect(mail_html.scan(/>Bob Marley/).count).to eq(1)
expect(mail_html.scan(/>bobmarley/).count).to eq(0)
SiteSetting.prioritize_username_in_ux = true
mail = UserNotifications.user_replied(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
mail_html = mail.html_part.body.to_s
expect(mail_html.scan(/>Bob Marley/).count).to eq(0)
expect(mail_html.scan(/>bobmarley/).count).to eq(1)
end
it "doesn't include details when private_email is enabled" do
SiteSetting.private_email = true
mail = UserNotifications.user_replied(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.html_part.body.to_s).to_not include(response.raw)
expect(mail.html_part.body.to_s).to_not include(topic.url)
expect(mail.text_part.to_s).to_not include(response.raw)
expect(mail.text_part.to_s).to_not include(topic.url)
end
it "includes excerpt when post_excerpts_in_emails is enabled" do
paragraphs = [
"This is the first paragraph, but you should read more.",
"And here is its friend, the second paragraph."
]
SiteSetting.post_excerpts_in_emails = true
SiteSetting.post_excerpt_maxlength = paragraphs.first.length
response.update!(raw: paragraphs.join("\n\n"))
mail = UserNotifications.user_replied(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
mail_html = mail.html_part.body.to_s
expect(mail_html.scan(/#{paragraphs[0]}/).count).to eq(1)
expect(mail_html.scan(/#{paragraphs[1]}/).count).to eq(0)
end
end
describe '.user_posted' do
let(:response_by_user) { Fabricate(:user, name: "John Doe", username: "john") }
let(:topic) { Fabricate(:topic, title: "Super cool topic") }
let(:post) { Fabricate(:post, topic: topic) }
let(:response) { Fabricate(:post, topic: topic, user: response_by_user) }
let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:posted_notification, user: user, post: response) }
it 'generates a correct email' do
SiteSetting.enable_names = false
mail = UserNotifications.user_posted(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
# from should not include full user name if "show user full names" is disabled
expect(mail[:from].display_names).to_not eql(['John Doe'])
# from should include username if "show user full names" is disabled
expect(mail[:from].display_names).to eql(['john via Discourse'])
# subject should not include category name
expect(mail.subject).not_to match(/Uncategorized/)
# 1 respond to links as no context by default
expect(mail.html_part.body.to_s.scan(/to respond/).count).to eq(1)
# 1 unsubscribe link
expect(mail.html_part.body.to_s.scan(/To unsubscribe/).count).to eq(1)
# side effect, topic user is updated with post number
tu = TopicUser.get(post.topic_id, user)
expect(tu.last_emailed_post_number).to eq(response.post_number)
end
it "doesn't include details when private_email is enabled" do
SiteSetting.private_email = true
mail = UserNotifications.user_posted(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.html_part.body.to_s).to_not include(response.raw)
expect(mail.text_part.to_s).to_not include(response.raw)
end
it "uses the original subject for staged users" do
incoming_email = Fabricate(
:incoming_email,
subject: "Original Subject",
post: post,
topic: post.topic,
user: user
)
mail = UserNotifications.user_posted(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to match(/Super cool topic/)
user.update!(staged: true)
mail = UserNotifications.user_posted(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to eq("Re: Original Subject")
another_post = Fabricate(:post, topic: topic)
incoming_email.update!(post_id: another_post.id)
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to match(/Super cool topic/)
end
end
describe '.user_private_message' do
let(:response_by_user) { Fabricate(:user, name: "", username: "john") }
let(:topic) { Fabricate(:private_message_topic, title: "Super cool topic") }
let(:post) { Fabricate(:post, topic: topic) }
let(:response) { Fabricate(:post, topic: topic, user: response_by_user) }
let(:user) { Fabricate(:user) }
let(:notification) { Fabricate(:private_message_notification, user: user, post: response) }
it 'generates a correct email' do
SiteSetting.enable_names = true
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
# from should include username if full user name is not provided
expect(mail[:from].display_names).to eql(['john via Discourse'])
# subject should include "[PM]"
expect(mail.subject).to include("[PM] ")
# 1 "visit message" link
expect(mail.html_part.body.to_s.scan(/Visit Message/).count).to eq(1)
# 1 respond to link
expect(mail.html_part.body.to_s.scan(/to respond/).count).to eq(1)
# 1 unsubscribe link
expect(mail.html_part.body.to_s.scan(/To unsubscribe/).count).to eq(1)
# side effect, topic user is updated with post number
tu = TopicUser.get(topic.id, user)
expect(tu.last_emailed_post_number).to eq(response.post_number)
end
it "doesn't include details when private_email is enabled" do
SiteSetting.private_email = true
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.html_part.body.to_s).to_not include(response.raw)
expect(mail.html_part.body.to_s).to_not include(topic.url)
expect(mail.text_part.to_s).to_not include(response.raw)
expect(mail.text_part.to_s).to_not include(topic.url)
end
it "doesn't include group name in subject" do
group = Fabricate(:group)
topic.allowed_groups = [group]
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to include("[PM] ")
end
it "includes a list of participants, groups first with member lists" do
group1 = Fabricate(:group, name: "group1")
group2 = Fabricate(:group, name: "group2")
user1 = Fabricate(:user, username: "one", groups: [group1, group2])
user2 = Fabricate(:user, username: "two", groups: [group1])
topic.allowed_users = [user1, user2]
topic.allowed_groups = [group1, group2]
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.body).to include("[group1 (2)](http://test.localhost/groups/group1), [group2 (1)](http://test.localhost/groups/group2), [one](http://test.localhost/u/one), [two](http://test.localhost/u/two)")
end
context "when SiteSetting.group_name_in_subject is true" do
before do
SiteSetting.group_in_subject = true
end
let(:group) { Fabricate(:group, name: "my_group") }
let(:mail) do
UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
end
shared_examples "includes first group name" do
it "includes first group name in subject" do
expect(mail.subject).to include("[my_group] ")
end
context "when first group has full name" do
it "includes full name in subject" do
group.full_name = "My Group"
group.save
expect(mail.subject).to include("[My Group] ")
end
end
end
context "one group in pm" do
before do
topic.allowed_groups = [group]
end
include_examples "includes first group name"
end
context "multiple groups in pm" do
let(:group2) { Fabricate(:group) }
before do
topic.allowed_groups = [group, group2]
end
include_examples "includes first group name"
end
context "no groups in pm" do
it "includes %{optional_pm} in subject" do
expect(mail.subject).to include("[PM] ")
end
end
end
it "uses the original subject for staged users when topic was started via email" do
incoming_email = Fabricate(
:incoming_email,
subject: "Original Subject",
post: post,
topic: topic,
user: user
)
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to match(/Super cool topic/)
user.update!(staged: true)
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to eq("Re: Original Subject")
another_post = Fabricate(:post, topic: topic)
incoming_email.update!(post_id: another_post.id)
mail = UserNotifications.user_private_message(
user,
post: response,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
expect(mail.subject).to match(/Super cool topic/)
end
end
it 'adds a warning when mail limit is reached' do
SiteSetting.max_emails_per_day_per_user = 2
user = Fabricate(:user)
user.email_logs.create!(
email_type: 'blah',
to_address: user.email,
user_id: user.id
)
post = Fabricate(:post)
reply = Fabricate(:post, topic_id: post.topic_id)
notification = Fabricate(
:notification,
topic_id: post.topic_id,
post_number: reply.post_number,
user: post.user,
data: { original_username: 'bob' }.to_json
)
mail = UserNotifications.user_replied(
user,
post: reply,
notification_type: notification.notification_type,
notification_data_hash: notification.data_hash
)
# WARNING: you reached the limit of 100 email notifications per day. Further emails will be suppressed.
# Consider watching less topics or disabling mailing list mode.
expect(mail.html_part.body.to_s).to match(I18n.t("user_notifications.reached_limit", count: 2))
expect(mail.body.to_s).to match(I18n.t("user_notifications.reached_limit", count: 2))
end
def expects_build_with(condition)
UserNotifications.any_instance.expects(:build_email).with(user.email, condition)
mailer = UserNotifications.public_send(
mail_type, user,
notification_type: Notification.types[notification.notification_type],
notification_data_hash: notification.data_hash,
post: notification.post
)
mailer.message
end
shared_examples "supports reply by email" do
context "reply_by_email" do
it "should have allow_reply_by_email set when that feature is enabled" do
expects_build_with(has_entry(:allow_reply_by_email, true))
end
end
end
shared_examples "no reply by email" do
context "reply_by_email" do
it "doesn't support reply by email" do
expects_build_with(Not(has_entry(:allow_reply_by_email, true)))
end
end
end
shared_examples "respect for private_email" do
context "private_email" do
it "doesn't support reply by email" do
SiteSetting.private_email = true
mailer = UserNotifications.public_send(
mail_type,
user,
notification_type: Notification.types[notification.notification_type],
notification_data_hash: notification.data_hash,
post: notification.post
)
message = mailer.message
topic = notification.post.topic
expect(message.html_part.body.to_s).not_to include(topic.title)
expect(message.html_part.body.to_s).not_to include(topic.slug)
expect(message.text_part.body.to_s).not_to include(topic.title)
expect(message.text_part.body.to_s).not_to include(topic.slug)
end
end
end
# The parts of emails that are derived from templates are translated
shared_examples "sets user locale" do
context "set locale for translating templates" do
it "sets the locale" do
expects_build_with(has_key(:locale))
end
end
end
shared_examples "notification email building" do
let(:post) { Fabricate(:post, user: user) }
let(:mail_type) { "user_#{notification_type}" }
let(:mail_template) { "user_notifications.#{mail_type}" }
let(:username) { "walterwhite" }
let(:notification) do
Fabricate(:notification,
user: user,
topic: post.topic,
notification_type: Notification.types[notification_type],
post_number: post.post_number,
data: { original_username: username }.to_json)
end
describe 'email building' do
it "has a username" do
expects_build_with(has_entry(:username, username))
end
it "has a url" do
expects_build_with(has_key(:url))
end
it "has a template" do
expects_build_with(has_entry(:template, mail_template))
end
it "overrides the html part" do
expects_build_with(has_key(:html_override))
end
it "has a message" do
expects_build_with(has_key(:message))
end
it "has a context" do
expects_build_with(has_key(:context))
end
it "has an unsubscribe link" do
expects_build_with(has_key(:add_unsubscribe_link))
end
it "has an post_id" do
expects_build_with(has_key(:post_id))
end
it "has an topic_id" do
expects_build_with(has_key(:topic_id))
end
it "should have user name as from_alias" do
SiteSetting.enable_names = true
SiteSetting.display_name_on_posts = true
expects_build_with(has_entry(:from_alias, "#{user.name} via Discourse"))
end
it "should not have user name as from_alias if display_name_on_posts is disabled" do
SiteSetting.enable_names = false
SiteSetting.display_name_on_posts = false
expects_build_with(has_entry(:from_alias, "walterwhite via Discourse"))
end
it "should explain how to respond" do
expects_build_with(Not(has_entry(:include_respond_instructions, false)))
end
it "should not explain how to respond if the user is suspended" do
User.any_instance.stubs(:suspended?).returns(true)
expects_build_with(has_entry(:include_respond_instructions, false))
end
context "when customized" do
let(:custom_body) do
body = +<<~BODY
You are now officially notified.
%{header_instructions}
%{message} %{respond_instructions}
%{topic_title_url_encoded}
%{site_title_url_encoded}
BODY
body << "%{context}" if notification_type != :invited_to_topic
body
end
before do
TranslationOverride.upsert!(
SiteSetting.default_locale,
"#{mail_template}.text_body_template",
custom_body
)
end
it "shouldn't use the default html_override" do
expects_build_with(Not(has_key(:html_override)))
end
end
end
end
describe "user mentioned email" do
include_examples "notification email building" do
let(:notification_type) { :mentioned }
include_examples "respect for private_email"
include_examples "supports reply by email"
include_examples "sets user locale"
end
end
describe "user replied" do
include_examples "notification email building" do
let(:notification_type) { :replied }
include_examples "respect for private_email"
include_examples "supports reply by email"
include_examples "sets user locale"
end
end
describe "user quoted" do
include_examples "notification email building" do
let(:notification_type) { :quoted }
include_examples "respect for private_email"
include_examples "supports reply by email"
include_examples "sets user locale"
end
end
describe "user posted" do
include_examples "notification email building" do
let(:notification_type) { :posted }
include_examples "respect for private_email"
include_examples "supports reply by email"
include_examples "sets user locale"
end
end
describe "user invited to a private message" do
include_examples "notification email building" do
let(:notification_type) { :invited_to_private_message }
let(:post) { Fabricate(:private_message_post) }
let(:user) { post.user }
let(:mail_template) { "user_notifications.user_#{notification_type}_pm" }
include_examples "respect for private_email"
include_examples "no reply by email"
include_examples "sets user locale"
end
end
describe "group invited to a private message" do
include_examples "notification email building" do
let(:notification_type) { :invited_to_private_message }
let(:post) { Fabricate(:private_message_post) }
let(:user) { post.user }
let(:group) { Fabricate(:group) }
let(:mail_template) { "user_notifications.user_#{notification_type}_pm_group" }
before do
notification.data_hash[:group_id] = group.id
notification.save!
end
it "should include the group name" do
expects_build_with(has_entry(:group_name, group.name))
end
include_examples "respect for private_email"
include_examples "no reply by email"
include_examples "sets user locale"
end
end
describe "user invited to a topic" do
include_examples "notification email building" do
let(:notification_type) { :invited_to_topic }
include_examples "respect for private_email"
include_examples "no reply by email"
include_examples "sets user locale"
end
end
describe "watching first post" do
include_examples "notification email building" do
let(:notification_type) { :invited_to_topic }
include_examples "respect for private_email"
include_examples "no reply by email"
include_examples "sets user locale"
end
end
# notification emails derived from templates are translated into the user's locale
shared_context "notification derived from template" do
let(:user) { Fabricate(:user, locale: locale) }
let(:mail_type) { mail_type }
let(:notification) { Fabricate(:notification, user: user) }
end
describe "notifications from template" do
context "user locale is allowed" do
before do
SiteSetting.allow_user_locale = true
end
%w(signup signup_after_approval confirm_old_email notify_old_email confirm_new_email
forgot_password admin_login account_created).each do |mail_type|
include_examples "notification derived from template" do
let(:locale) { "fr" }
let(:mail_type) { mail_type }
it "sets the locale" do
expects_build_with(has_entry(:locale, "fr"))
end
end
end
end
context "user locale is not allowed" do
before do
SiteSetting.allow_user_locale = false
end
%w(signup signup_after_approval notify_old_email confirm_old_email confirm_new_email
forgot_password admin_login account_created).each do |mail_type|
include_examples "notification derived from template" do
let(:locale) { "fr" }
let(:mail_type) { mail_type }
it "sets the locale" do
expects_build_with(has_entry(:locale, "en_US"))
end
end
end
end
end
end