From 4e3866dbaf1695d8d305127722cf5848fc9128cd Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 5 Mar 2026 09:42:21 -0500 Subject: [PATCH] Replace `email_spec` gem with built-in matchers (#38079) --- Gemfile | 3 - Gemfile.lock | 5 - spec/mailers/admin_mailer_spec.rb | 111 +++++++------ spec/mailers/notification_mailer_spec.rb | 74 +++++---- spec/mailers/user_mailer_spec.rb | 157 +++++++++--------- spec/rails_helper.rb | 1 - spec/support/examples/mailers.rb | 27 ++- spec/support/system_helpers.rb | 4 + .../admin/announcements/distributions_spec.rb | 7 +- spec/system/admin/announcements/tests_spec.rb | 6 +- spec/system/admin/change_emails_spec.rb | 7 +- spec/system/admin/confirmations_spec.rb | 7 +- spec/system/admin/reset_spec.rb | 17 +- .../terms_of_service/distributions_spec.rb | 10 +- .../admin/terms_of_service/tests_spec.rb | 6 +- spec/system/auth/passwords_spec.rb | 5 +- spec/system/disputes/appeals_spec.rb | 14 +- .../two_factor_authentication_methods_spec.rb | 11 +- 18 files changed, 231 insertions(+), 241 deletions(-) diff --git a/Gemfile b/Gemfile index 4c47b0861c..1ff1ebf7de 100644 --- a/Gemfile +++ b/Gemfile @@ -129,9 +129,6 @@ group :test do # Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab gem 'rspec-github', '~> 3.0', require: false - # RSpec helpers for email specs - gem 'email_spec' - # Extra RSpec extension methods and helpers for sidekiq gem 'rspec-sidekiq', '~> 5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 56be5db8f0..63f8baeea0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -224,10 +224,6 @@ GEM base64 faraday (>= 1, < 3) multi_json - email_spec (2.3.0) - htmlentities (~> 4.3.3) - launchy (>= 2.1, < 4.0) - mail (~> 2.7) email_validator (2.2.4) activemodel erb (6.0.2) @@ -977,7 +973,6 @@ DEPENDENCIES discard (~> 1.2) doorkeeper (~> 5.6) dotenv - email_spec fabrication faker (~> 3.2) faraday-httpclient diff --git a/spec/mailers/admin_mailer_spec.rb b/spec/mailers/admin_mailer_spec.rb index 6d11758add..e71a8308bf 100644 --- a/spec/mailers/admin_mailer_spec.rb +++ b/spec/mailers/admin_mailer_spec.rb @@ -14,12 +14,16 @@ RSpec.describe AdminMailer do end it 'renders the email' do - expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.new_report.subject', instance: Rails.configuration.x.local_domain, id: report.id))) - .and(have_body_text("Mike,\r\n\r\nJohn has reported Mike\r\n\r\nView: #{admin_report_url(report)}\r\n")) + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.new_report.subject', instance: Rails.configuration.x.local_domain, id: report.id) + ) + expect(mail.body) + .to match('Mike,') + .and match('John has reported Mike') + .and match("View: #{admin_report_url(report)}") end end @@ -33,12 +37,14 @@ RSpec.describe AdminMailer do end it 'renders the email' do - expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.new_appeal.subject', instance: Rails.configuration.x.local_domain, username: appeal.account.username))) - .and(have_body_text("#{appeal.account.username} is appealing a moderation decision by #{appeal.strike.account.username}")) + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.new_appeal.subject', instance: Rails.configuration.x.local_domain, username: appeal.account.username) + ) + expect(mail.body) + .to match("#{appeal.account.username} is appealing a moderation decision by #{appeal.strike.account.username}") end end @@ -52,12 +58,14 @@ RSpec.describe AdminMailer do end it 'renders the email' do - expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.new_pending_account.subject', instance: Rails.configuration.x.local_domain, username: user.account.username))) - .and(have_body_text('The details of the new account are below. You can approve or reject this application.')) + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.new_pending_account.subject', instance: Rails.configuration.x.local_domain, username: user.account.username) + ) + expect(mail.body) + .to match('The details of the new account are below. You can approve or reject this application.') end end @@ -76,15 +84,17 @@ RSpec.describe AdminMailer do end it 'renders the email' do - expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.new_trends.subject', instance: Rails.configuration.x.local_domain))) - .and(have_body_text('The following items need a review before they can be displayed publicly')) - .and(have_body_text(ActivityPub::TagManager.instance.url_for(status))) - .and(have_body_text(link.title)) - .and(have_body_text(tag.display_name)) + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.new_trends.subject', instance: Rails.configuration.x.local_domain) + ) + expect(mail.body) + .to match('The following items need a review before they can be displayed publicly') + .and match(ActivityPub::TagManager.instance.url_for(status)) + .and match(link.title) + .and match(tag.display_name) end end @@ -97,12 +107,14 @@ RSpec.describe AdminMailer do end it 'renders the email' do - expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.new_software_updates.subject', instance: Rails.configuration.x.local_domain))) - .and(have_body_text('New Mastodon versions have been released, you may want to update!')) + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.new_software_updates.subject', instance: Rails.configuration.x.local_domain) + ) + expect(mail.body) + .to match('New Mastodon versions have been released, you may want to update!') end end @@ -115,15 +127,18 @@ RSpec.describe AdminMailer do end it 'renders the email' do + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.new_critical_software_updates.subject', instance: Rails.configuration.x.local_domain) + ) + expect(mail.body) + .to match('New critical versions of Mastodon have been released, you may want to update as soon as possible!') expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.new_critical_software_updates.subject', instance: Rails.configuration.x.local_domain))) - .and(have_body_text('New critical versions of Mastodon have been released, you may want to update as soon as possible!')) - .and(have_header('Importance', 'high')) - .and(have_header('Priority', 'urgent')) - .and(have_header('X-Priority', '1')) + .to have_header('Importance', 'high') + .and have_header('Priority', 'urgent') + .and have_header('X-Priority', '1') end end @@ -136,12 +151,14 @@ RSpec.describe AdminMailer do end it 'renders the email' do - expect(mail) - .to be_present - .and(deliver_to(recipient.user_email)) - .and(deliver_from('notifications@localhost')) - .and(have_subject(I18n.t('admin_mailer.auto_close_registrations.subject', instance: Rails.configuration.x.local_domain))) - .and(have_body_text('have been automatically switched')) + expect { mail.deliver } + .to send_email( + to: recipient.user_email, + from: 'notifications@localhost', + subject: I18n.t('admin_mailer.auto_close_registrations.subject', instance: Rails.configuration.x.local_domain) + ) + expect(mail.body) + .to match('have been automatically switched') end end end diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index b88277367d..733cbaa615 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -38,12 +38,15 @@ RSpec.describe NotificationMailer do it_behaves_like 'localized subject', 'notification_mailer.mention.subject', name: 'bob' it 'renders the email' do + expect { mail.deliver } + .to send_email( + subject: 'You were mentioned by bob' + ) + expect(mail.text_part.body) + .to match('You were mentioned by bob') + .and match('The body of the foreign status') expect(mail) - .to be_present - .and(have_subject('You were mentioned by bob')) - .and(have_body_text('You were mentioned by bob')) - .and(have_body_text('The body of the foreign status')) - .and have_thread_headers + .to have_thread_headers .and have_standard_headers('mention').for(receiver) end @@ -59,12 +62,15 @@ RSpec.describe NotificationMailer do it_behaves_like 'localized subject', 'notification_mailer.quote.subject', name: 'bob' it 'renders the email' do + expect { mail.deliver } + .to send_email( + subject: 'bob quoted your post' + ) + expect(mail.text_part.body) + .to match('Your post was quoted by bob') + .and match('The body of the foreign status') expect(mail) - .to be_present - .and(have_subject('bob quoted your post')) - .and(have_body_text('Your post was quoted by bob')) - .and(have_body_text('The body of the foreign status')) - .and have_thread_headers + .to have_thread_headers .and have_standard_headers('quote').for(receiver) end @@ -80,11 +86,14 @@ RSpec.describe NotificationMailer do it_behaves_like 'localized subject', 'notification_mailer.follow.subject', name: 'bob' it 'renders the email' do + expect { mail.deliver } + .to send_email( + subject: 'bob is now following you' + ) + expect(mail.text_part.body) + .to match('bob is now following you') expect(mail) - .to be_present - .and(have_subject('bob is now following you')) - .and(have_body_text('bob is now following you')) - .and have_standard_headers('follow').for(receiver) + .to have_standard_headers('follow').for(receiver) end it_behaves_like 'delivery to non functional user' @@ -98,12 +107,15 @@ RSpec.describe NotificationMailer do it_behaves_like 'localized subject', 'notification_mailer.favourite.subject', name: 'bob' it 'renders the email' do + expect { mail.deliver } + .to send_email( + subject: 'bob favorited your post' + ) + expect(mail.text_part.body) + .to match('Your post was favorited by bob') + .and match('The body of the own status') expect(mail) - .to be_present - .and(have_subject('bob favorited your post')) - .and(have_body_text('Your post was favorited by bob')) - .and(have_body_text('The body of the own status')) - .and have_thread_headers + .to have_thread_headers .and have_standard_headers('favourite').for(receiver) end @@ -119,12 +131,15 @@ RSpec.describe NotificationMailer do it_behaves_like 'localized subject', 'notification_mailer.reblog.subject', name: 'bob' it 'renders the email' do + expect { mail.deliver } + .to send_email( + subject: 'bob boosted your post' + ) + expect(mail.text_part.body) + .to match('Your post was boosted by bob') + .and match('The body of the own status') expect(mail) - .to be_present - .and(have_subject('bob boosted your post')) - .and(have_body_text('Your post was boosted by bob')) - .and(have_body_text('The body of the own status')) - .and have_thread_headers + .to have_thread_headers .and have_standard_headers('reblog').for(receiver) end @@ -140,11 +155,14 @@ RSpec.describe NotificationMailer do it_behaves_like 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob' it 'renders the email' do + expect { mail.deliver } + .to send_email( + subject: 'Pending follower: bob' + ) + expect(mail.text_part.body) + .to match('bob has requested to follow you') expect(mail) - .to be_present - .and(have_subject('Pending follower: bob')) - .and(have_body_text('bob has requested to follow you')) - .and have_standard_headers('follow_request').for(receiver) + .to have_standard_headers('follow_request').for(receiver) end it_behaves_like 'delivery to non functional user' diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 153de1ef1c..75df58c6bf 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -8,8 +8,8 @@ RSpec.describe UserMailer do before { receiver.account.update(memorial: true) } it 'does not deliver mail' do - emails = capture_emails { mail.deliver_now } - expect(emails).to be_empty + expect { mail.deliver_now } + .to_not send_email end end end @@ -59,11 +59,10 @@ RSpec.describe UserMailer do it 'renders confirmation instructions' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('devise.mailer.confirmation_instructions.title'))) - .and(have_body_text('spec')) - .and(have_body_text(Rails.configuration.x.local_domain)) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.confirmation_instructions.title')) + .and match('spec') + .and match(Rails.configuration.x.local_domain) end it_behaves_like 'localized subject', @@ -78,11 +77,10 @@ RSpec.describe UserMailer do it 'renders reconfirmation instructions' do receiver.update!(email: 'new-email@example.com', locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('devise.mailer.reconfirmation_instructions.title'))) - .and(have_body_text('spec')) - .and(have_body_text(Rails.configuration.x.local_domain)) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.reconfirmation_instructions.title')) + .and match('spec') + .and match(Rails.configuration.x.local_domain) end it_behaves_like 'localized subject', @@ -97,10 +95,9 @@ RSpec.describe UserMailer do it 'renders reset password instructions' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('devise.mailer.reset_password_instructions.title'))) - .and(have_body_text('spec')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.reset_password_instructions.title')) + .and match('spec') end it_behaves_like 'localized subject', @@ -114,9 +111,8 @@ RSpec.describe UserMailer do it 'renders password change notification' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('devise.mailer.password_change.title'))) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.password_change.title')) end it_behaves_like 'localized subject', @@ -130,9 +126,8 @@ RSpec.describe UserMailer do it 'renders email change notification' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('devise.mailer.email_changed.title'))) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.email_changed.title')) end it_behaves_like 'localized subject', @@ -149,10 +144,9 @@ RSpec.describe UserMailer do it 'renders warning notification' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('user_mailer.warning.title.suspend', acct: receiver.account.acct))) - .and(have_body_text(strike.text)) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.warning.title.suspend', acct: receiver.account.acct)) + .and match(strike.text) end end @@ -163,9 +157,8 @@ RSpec.describe UserMailer do it 'renders webauthn credential deleted notification' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('devise.mailer.webauthn_credential.deleted.title'))) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.webauthn_credential.deleted.title')) end it_behaves_like 'localized subject', @@ -182,9 +175,8 @@ RSpec.describe UserMailer do it 'renders suspicious sign in notification' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('user_mailer.suspicious_sign_in.explanation'))) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.suspicious_sign_in.explanation')) end it_behaves_like 'localized subject', @@ -200,9 +192,8 @@ RSpec.describe UserMailer do it 'renders failed 2FA notification' do receiver.update!(locale: nil) - expect(mail) - .to be_present - .and(have_body_text(I18n.t('user_mailer.failed_2fa.explanation'))) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.failed_2fa.explanation')) end it_behaves_like 'localized subject', @@ -214,10 +205,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.appeal_approved(receiver, appeal) } it 'renders appeal_approved notification' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at)))) - .and(have_body_text(I18n.t('user_mailer.appeal_approved.title'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at))) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.appeal_approved.title')) end end @@ -226,10 +217,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.appeal_rejected(receiver, appeal) } it 'renders appeal_rejected notification' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at)))) - .and(have_body_text(I18n.t('user_mailer.appeal_rejected.title'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at))) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.appeal_rejected.title')) end end @@ -237,10 +228,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.two_factor_enabled(receiver) } it 'renders two_factor_enabled mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('devise.mailer.two_factor_enabled.subject'))) - .and(have_body_text(I18n.t('devise.mailer.two_factor_enabled.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('devise.mailer.two_factor_enabled.subject')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.two_factor_enabled.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -250,10 +241,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.two_factor_disabled(receiver) } it 'renders two_factor_disabled mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('devise.mailer.two_factor_disabled.subject'))) - .and(have_body_text(I18n.t('devise.mailer.two_factor_disabled.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('devise.mailer.two_factor_disabled.subject')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.two_factor_disabled.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -263,10 +254,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.webauthn_enabled(receiver) } it 'renders webauthn_enabled mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('devise.mailer.webauthn_enabled.subject'))) - .and(have_body_text(I18n.t('devise.mailer.webauthn_enabled.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('devise.mailer.webauthn_enabled.subject')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.webauthn_enabled.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -276,10 +267,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.webauthn_disabled(receiver) } it 'renders webauthn_disabled mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('devise.mailer.webauthn_disabled.subject'))) - .and(have_body_text(I18n.t('devise.mailer.webauthn_disabled.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('devise.mailer.webauthn_disabled.subject')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.webauthn_disabled.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -289,10 +280,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.two_factor_recovery_codes_changed(receiver) } it 'renders two_factor_recovery_codes_changed mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('devise.mailer.two_factor_recovery_codes_changed.subject'))) - .and(have_body_text(I18n.t('devise.mailer.two_factor_recovery_codes_changed.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('devise.mailer.two_factor_recovery_codes_changed.subject')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.two_factor_recovery_codes_changed.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -303,10 +294,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.webauthn_credential_added(receiver, credential) } it 'renders webauthn_credential_added mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('devise.mailer.webauthn_credential.added.subject'))) - .and(have_body_text(I18n.t('devise.mailer.webauthn_credential.added.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('devise.mailer.webauthn_credential.added.subject')) + expect(mail.text_part.body) + .to match(I18n.t('devise.mailer.webauthn_credential.added.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -322,10 +313,10 @@ RSpec.describe UserMailer do end it 'renders welcome mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('user_mailer.welcome.subject'))) - .and(have_body_text(I18n.t('user_mailer.welcome.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('user_mailer.welcome.subject')) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.welcome.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -336,10 +327,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.backup_ready(receiver, backup) } it 'renders backup_ready mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('user_mailer.backup_ready.subject'))) - .and(have_body_text(I18n.t('user_mailer.backup_ready.explanation'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('user_mailer.backup_ready.subject')) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.backup_ready.explanation')) end it_behaves_like 'delivery to memorialized user' @@ -350,10 +341,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.terms_of_service_changed(receiver, terms) } it 'renders terms_of_service_changed mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('user_mailer.terms_of_service_changed.subject'))) - .and(have_body_text(I18n.t('user_mailer.terms_of_service_changed.changelog'))) + expect { mail.deliver } + .to send_email(subject: I18n.t('user_mailer.terms_of_service_changed.subject')) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.terms_of_service_changed.changelog')) end it_behaves_like 'optional bulk mailer settings' @@ -364,10 +355,10 @@ RSpec.describe UserMailer do let(:mail) { described_class.announcement_published(receiver, announcement) } it 'renders announcement_published mail' do - expect(mail) - .to be_present - .and(have_subject(I18n.t('user_mailer.announcement_published.subject'))) - .and(have_body_text(I18n.t('user_mailer.announcement_published.description', domain: local_domain_uri.host))) + expect { mail.deliver } + .to send_email(subject: I18n.t('user_mailer.announcement_published.subject')) + expect(mail.text_part.body) + .to match(I18n.t('user_mailer.announcement_published.description', domain: local_domain_uri.host)) end it_behaves_like 'optional bulk mailer settings' diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index bc12d1bece..3316f8208d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -43,7 +43,6 @@ require 'webmock/rspec' require 'paperclip/matchers' require 'capybara/rspec' require 'chewy/rspec' -require 'email_spec/rspec' require 'pundit/rspec' require 'test_prof/recipes/rspec/before_all' diff --git a/spec/support/examples/mailers.rb b/spec/support/examples/mailers.rb index bf7963a6b8..9f6ae3b29d 100644 --- a/spec/support/examples/mailers.rb +++ b/spec/support/examples/mailers.rb @@ -17,8 +17,8 @@ RSpec::Matchers.define :have_thread_headers do match(notify_expectation_failures: true) do |mail| expect(mail) .to be_present - .and(have_header('In-Reply-To', conversation_header_regex)) - .and(have_header('References', conversation_header_regex)) + .and have_header('In-Reply-To', conversation_header_regex) + .and have_header('References', conversation_header_regex) end def conversation_header_regex = // @@ -32,12 +32,21 @@ RSpec::Matchers.define :have_standard_headers do |type| match(notify_expectation_failures: true) do |mail| expect(mail) .to be_present - .and(have_header('To', "#{@user.account.username} <#{@user.email}>")) - .and(have_header('List-ID', "<#{type}.#{@user.account.username}.#{Rails.configuration.x.local_domain}>")) - .and(have_header('List-Unsubscribe', %r{})) - .and(have_header('List-Unsubscribe', /&type=#{type}/)) - .and(have_header('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click')) - .and(deliver_to("#{@user.account.username} <#{@user.email}>")) - .and(deliver_from(Rails.configuration.action_mailer.default_options[:from])) + .and have_header('To', "#{@user.account.username} <#{@user.email}>") + .and have_header('List-ID', "<#{type}.#{@user.account.username}.#{Rails.configuration.x.local_domain}>") + .and have_header('List-Unsubscribe', %r{}) + .and have_header('List-Unsubscribe', /&type=#{type}/) + .and have_header('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click') + expect(mail.to) + .to contain_exactly(@user.email) + expect(mail.from) + .to contain_exactly(Rails.configuration.action_mailer.default_options[:from]) + end +end + +RSpec::Matchers.define :have_header do |name, value| + match(notify_expectation_failures: true) do |mail| + expect(mail.header[name].value) + .to match(value) end end diff --git a/spec/support/system_helpers.rb b/spec/support/system_helpers.rb index 44bbc64a59..9e47b3f247 100644 --- a/spec/support/system_helpers.rb +++ b/spec/support/system_helpers.rb @@ -22,4 +22,8 @@ module SystemHelpers def frontend_translations(key) FRONTEND_TRANSLATIONS[key] end + + def email_links(email) + URI.extract(email.text_part.to_s, %w(http https)) + end end diff --git a/spec/system/admin/announcements/distributions_spec.rb b/spec/system/admin/announcements/distributions_spec.rb index a503d466b0..a79be6c551 100644 --- a/spec/system/admin/announcements/distributions_spec.rb +++ b/spec/system/admin/announcements/distributions_spec.rb @@ -14,13 +14,10 @@ RSpec.describe 'Admin Announcement Mail Distributions' do expect(page) .to have_title(I18n.t('admin.announcements.preview.title')) - emails = capture_emails do + expect do expect { click_on I18n.t('admin.terms_of_service.preview.send_to_all', count: 1, display_count: 1) } .to(change { announcement.reload.notification_sent_at }) - end - expect(emails.first) - .to be_present - .and(deliver_to(user.email)) + end.to send_email(to: user.email) expect(page) .to have_title(I18n.t('admin.announcements.title')) end diff --git a/spec/system/admin/announcements/tests_spec.rb b/spec/system/admin/announcements/tests_spec.rb index 00767c06e8..8601defcc9 100644 --- a/spec/system/admin/announcements/tests_spec.rb +++ b/spec/system/admin/announcements/tests_spec.rb @@ -14,10 +14,8 @@ RSpec.describe 'Admin TermsOfService Tests' do expect(page) .to have_title(I18n.t('admin.announcements.preview.title')) - emails = capture_emails { click_on I18n.t('admin.terms_of_service.preview.send_preview', email: user.email) } - expect(emails.first) - .to be_present - .and(deliver_to(user.email)) + expect { click_on I18n.t('admin.terms_of_service.preview.send_preview', email: user.email) } + .to send_email(to: user.email) expect(page) .to have_title(I18n.t('admin.announcements.title')) end diff --git a/spec/system/admin/change_emails_spec.rb b/spec/system/admin/change_emails_spec.rb index 6592ddff7c..f4d0be4c0c 100644 --- a/spec/system/admin/change_emails_spec.rb +++ b/spec/system/admin/change_emails_spec.rb @@ -16,11 +16,8 @@ RSpec.describe 'Admin Change Emails' do .to have_title(I18n.t('admin.accounts.change_email.title', username: user.account.username)) fill_in 'user_unconfirmed_email', with: 'test@host.example' - emails = capture_emails { process_change_email } - expect(emails.first) - .to be_present - .and(deliver_to('test@host.example')) - .and(have_subject(/Confirm email/)) + expect { process_change_email } + .to send_email(to: 'test@host.example', subject: /Confirm email/) expect(page) .to have_title(user.account.pretty_acct) end diff --git a/spec/system/admin/confirmations_spec.rb b/spec/system/admin/confirmations_spec.rb index 70821af701..b0f2eed144 100644 --- a/spec/system/admin/confirmations_spec.rb +++ b/spec/system/admin/confirmations_spec.rb @@ -36,14 +36,11 @@ RSpec.describe 'Admin Confirmations' do it 'resends the confirmation mail' do visit admin_account_path(id: user.account.id) - emails = capture_emails { resend_confirmation } + expect { resend_confirmation } + .to send_email(to: user.email) expect(page) .to have_title(I18n.t('admin.accounts.title')) .and have_content(I18n.t('admin.accounts.resend_confirmation.success')) - - expect(emails.first) - .to be_present - .and deliver_to(user.email) end end diff --git a/spec/system/admin/reset_spec.rb b/spec/system/admin/reset_spec.rb index 5cd0c048bb..77f97c2eeb 100644 --- a/spec/system/admin/reset_spec.rb +++ b/spec/system/admin/reset_spec.rb @@ -8,20 +8,11 @@ RSpec.describe 'Admin::Reset' do sign_in admin_user visit admin_account_path(account.id) - emails = capture_emails do + expect do expect { submit_reset } - .to change(Admin::ActionLog.where(target: account.user), :count).by(1) - end - - expect(emails.first) - .to be_present - .and(deliver_to(account.user.email)) - .and(have_subject(password_change_subject)) - - expect(emails.last) - .to be_present - .and(deliver_to(account.user.email)) - .and(have_subject(reset_instructions_subject)) + .to send_email(to: account.user.email, subject: password_change_subject) + .and send_email(to: account.user.email, subject: reset_instructions_subject) + end.to change(Admin::ActionLog.where(target: account.user), :count).by(1) expect(page) .to have_content(account.username) diff --git a/spec/system/admin/terms_of_service/distributions_spec.rb b/spec/system/admin/terms_of_service/distributions_spec.rb index ba525d09c0..d094299a60 100644 --- a/spec/system/admin/terms_of_service/distributions_spec.rb +++ b/spec/system/admin/terms_of_service/distributions_spec.rb @@ -14,13 +14,9 @@ RSpec.describe 'Admin TermsOfService Distributions' do expect(page) .to have_title(I18n.t('admin.terms_of_service.preview.title')) - emails = capture_emails do - expect { click_on I18n.t('admin.terms_of_service.preview.send_to_all', count: 1, display_count: 1) } - .to(change { terms_of_service.reload.notification_sent_at }) - end - expect(emails.first) - .to be_present - .and(deliver_to(user.email)) + expect { click_on I18n.t('admin.terms_of_service.preview.send_to_all', count: 1, display_count: 1) } + .to change { terms_of_service.reload.notification_sent_at } + .and send_email(to: user.email) expect(page) .to have_title(I18n.t('admin.terms_of_service.title')) end diff --git a/spec/system/admin/terms_of_service/tests_spec.rb b/spec/system/admin/terms_of_service/tests_spec.rb index 3fc7d4e75d..f00700616b 100644 --- a/spec/system/admin/terms_of_service/tests_spec.rb +++ b/spec/system/admin/terms_of_service/tests_spec.rb @@ -14,10 +14,8 @@ RSpec.describe 'Admin TermsOfService Tests' do expect(page) .to have_title(I18n.t('admin.terms_of_service.preview.title')) - emails = capture_emails { click_on I18n.t('admin.terms_of_service.preview.send_preview', email: user.email) } - expect(emails.first) - .to be_present - .and(deliver_to(user.email)) + expect { click_on I18n.t('admin.terms_of_service.preview.send_preview', email: user.email) } + .to send_email(to: user.email) expect(page) .to have_title(I18n.t('admin.terms_of_service.preview.title')) end diff --git a/spec/system/auth/passwords_spec.rb b/spec/system/auth/passwords_spec.rb index 42733b2521..83853d68fa 100644 --- a/spec/system/auth/passwords_spec.rb +++ b/spec/system/auth/passwords_spec.rb @@ -50,9 +50,8 @@ RSpec.describe 'Auth Passwords' do def submit_email_reset fill_in 'user_email', with: user.email - click_on I18n.t('auth.reset_password') - open_last_email - visit_in_email(I18n.t('devise.mailer.reset_password_instructions.action')) + emails = capture_emails { click_on I18n.t('auth.reset_password') } + visit email_links(emails.first).first end def set_new_password diff --git a/spec/system/disputes/appeals_spec.rb b/spec/system/disputes/appeals_spec.rb index 860b8fcfd1..0b601c4584 100644 --- a/spec/system/disputes/appeals_spec.rb +++ b/spec/system/disputes/appeals_spec.rb @@ -27,17 +27,9 @@ RSpec.describe 'Dispute Appeals' do # Valid with text fill_in 'appeal_text', with: 'It wasnt me this time!' - emails = capture_emails do - expect { submit_form } - .to change(Appeal, :count).by(1) - end - expect(emails) - .to contain_exactly( - have_attributes( - to: contain_exactly(admin.email), - subject: eq(new_appeal_subject) - ) - ) + expect { submit_form } + .to change(Appeal, :count).by(1) + .and send_email(to: admin.email, subject: new_appeal_subject) expect(page) .to have_content(I18n.t('disputes.strikes.appealed_msg')) end diff --git a/spec/system/settings/two_factor_authentication_methods_spec.rb b/spec/system/settings/two_factor_authentication_methods_spec.rb index bed226deb5..867c39e376 100644 --- a/spec/system/settings/two_factor_authentication_methods_spec.rb +++ b/spec/system/settings/two_factor_authentication_methods_spec.rb @@ -24,17 +24,12 @@ RSpec.describe 'Settings TwoFactorAuthenticationMethods' do # Fill in challenge form fill_in 'form_challenge_current_password', with: user.password - emails = capture_emails do - expect { click_on I18n.t('challenge.confirm') } - .to change { user.reload.otp_required_for_login }.to(false) - end + expect { click_on I18n.t('challenge.confirm') } + .to change { user.reload.otp_required_for_login }.to(false) + .and send_email(to: user.email, subject: I18n.t('devise.mailer.two_factor_disabled.subject')) expect(page) .to have_content(I18n.t('two_factor_authentication.disabled_success')) - expect(emails.first) - .to be_present - .and(deliver_to(user.email)) - .and(have_subject(I18n.t('devise.mailer.two_factor_disabled.subject'))) end end end