mirror of
https://github.com/astuto/astuto.git
synced 2025-12-15 19:27:52 +01:00
Send recap emails for new feedback (#440)
This commit is contained in:
committed by
GitHub
parent
ace50e1089
commit
c0d70186f6
3
Gemfile
3
Gemfile
@@ -64,6 +64,9 @@ gem 'rack-cors', '2.0.2'
|
||||
# ActiveJob backend
|
||||
gem 'sidekiq', '7.3.5'
|
||||
|
||||
# Cron jobs with sidekiq
|
||||
gem 'sidekiq-cron', '2.0.1'
|
||||
|
||||
group :development, :test do
|
||||
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
|
||||
|
||||
|
||||
16
Gemfile.lock
16
Gemfile.lock
@@ -85,6 +85,9 @@ GEM
|
||||
concurrent-ruby (1.3.4)
|
||||
connection_pool (2.4.1)
|
||||
crass (1.0.6)
|
||||
cronex (0.15.0)
|
||||
tzinfo
|
||||
unicode (>= 0.4.4.5)
|
||||
cssbundling-rails (1.1.2)
|
||||
railties (>= 6.0.0)
|
||||
date (3.4.0)
|
||||
@@ -96,6 +99,8 @@ GEM
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.5.1)
|
||||
erubi (1.13.0)
|
||||
et-orbi (1.2.11)
|
||||
tzinfo
|
||||
execjs (2.10.0)
|
||||
factory_bot (5.0.2)
|
||||
activesupport (>= 4.2.0)
|
||||
@@ -105,6 +110,9 @@ GEM
|
||||
ffi (1.17.0)
|
||||
friendly_id (5.5.1)
|
||||
activerecord (>= 4.0.0)
|
||||
fugit (1.11.1)
|
||||
et-orbi (~> 1, >= 1.2.11)
|
||||
raabro (~> 1.4)
|
||||
globalid (1.2.1)
|
||||
activesupport (>= 6.1)
|
||||
httparty (0.21.0)
|
||||
@@ -173,6 +181,7 @@ GEM
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.2.0)
|
||||
activesupport (>= 3.0.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.8.1)
|
||||
rack (2.2.10)
|
||||
rack-attack (6.7.0)
|
||||
@@ -264,6 +273,11 @@ GEM
|
||||
logger
|
||||
rack (>= 2.2.4)
|
||||
redis-client (>= 0.22.2)
|
||||
sidekiq-cron (2.0.1)
|
||||
cronex (>= 0.13.0)
|
||||
fugit (~> 1.8, >= 1.11.1)
|
||||
globalid (>= 1.0.1)
|
||||
sidekiq (>= 6.5.0)
|
||||
spring (2.1.1)
|
||||
spring-watcher-listen (2.0.1)
|
||||
listen (>= 2.7, < 4.0)
|
||||
@@ -284,6 +298,7 @@ GEM
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode (0.4.4.5)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
web-console (4.2.1)
|
||||
@@ -330,6 +345,7 @@ DEPENDENCIES
|
||||
rswag-specs (= 2.15.0)
|
||||
selenium-webdriver (= 4.17.0)
|
||||
sidekiq (= 7.3.5)
|
||||
sidekiq-cron (= 2.0.1)
|
||||
spring (= 2.1.1)
|
||||
spring-watcher-listen (= 2.0.1)
|
||||
stripe (= 11.2.0)
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
padding: 15px;
|
||||
margin: 0 auto;
|
||||
|
||||
label[for=user_notifications_enabled] {
|
||||
@extend .mb-0;
|
||||
}
|
||||
}
|
||||
|
||||
.apiKeyGenerateButton { width: 100%; }
|
||||
|
||||
@@ -35,7 +35,12 @@ class ApplicationController < ActionController::Base
|
||||
protected
|
||||
|
||||
def configure_devise_permitted_parameters
|
||||
additional_permitted_parameters = [:full_name, :notifications_enabled, :invitation_token]
|
||||
additional_permitted_parameters = [
|
||||
:full_name,
|
||||
:notifications_enabled,
|
||||
:recap_notification_frequency,
|
||||
:invitation_token
|
||||
]
|
||||
|
||||
devise_parameter_sanitizer.permit(:sign_up, keys: additional_permitted_parameters)
|
||||
devise_parameter_sanitizer.permit(:account_update, keys: additional_permitted_parameters)
|
||||
|
||||
@@ -43,7 +43,8 @@ class TenantsController < ApplicationController
|
||||
email: params[:user][:email],
|
||||
password: is_o_auth_login ? Devise.friendly_token : params[:user][:password],
|
||||
has_set_password: !is_o_auth_login,
|
||||
role: "owner"
|
||||
role: "owner",
|
||||
recap_notification_frequency: "daily"
|
||||
)
|
||||
|
||||
if is_o_auth_login
|
||||
|
||||
87
app/jobs/send_recap_emails.rb
Normal file
87
app/jobs/send_recap_emails.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
class SendRecapEmails < ActiveJob::Base
|
||||
queue_as :default
|
||||
|
||||
def perform(*args)
|
||||
# Fix times to 15:00 UTC
|
||||
time_now = Time.now.utc.change(hour: args[0], min: 0, sec: 0)
|
||||
one_day_ago = 1.day.ago.utc.change(hour: args[0], min: 0, sec: 0)
|
||||
one_week_ago = 1.week.ago.utc.change(hour: args[0], min: 0, sec: 0)
|
||||
one_month_ago = 1.month.ago.utc.change(hour: args[0], min: 0, sec: 0)
|
||||
|
||||
# Get tenants with active subscriptions
|
||||
tbs = TenantBilling.unscoped.all
|
||||
tbs = tbs.select { |tb| tb.has_active_subscription? }
|
||||
tenants = Tenant.where(id: tbs.map(&:tenant_id))
|
||||
|
||||
# Based on the current date, determine which recap notifications to send
|
||||
frequencies_to_notify = ['daily']
|
||||
frequencies_to_notify.push('weekly') if Date.today.monday? # Send weekly recap on Mondays
|
||||
frequencies_to_notify.push('monthly') if Date.today.day == 1 # Send monthly recap on the 1st of the month
|
||||
|
||||
tenants.each do |tenant|
|
||||
Current.tenant = tenant
|
||||
I18n.locale = tenant.locale
|
||||
|
||||
# Get users with recap notifications enabled
|
||||
users = tenant.users.where(
|
||||
role: ['owner', 'admin', 'moderator'],
|
||||
notifications_enabled: true,
|
||||
recap_notification_frequency: frequencies_to_notify,
|
||||
)
|
||||
|
||||
# Get the different recap notification frequencies for users
|
||||
users_recap_notification_frequencies = users.map(&:recap_notification_frequency).flatten.uniq
|
||||
|
||||
# Get only needed posts
|
||||
if users_recap_notification_frequencies.include?('daily')
|
||||
published_posts_daily = Post.where(approval_status: 'approved', created_at: one_day_ago..time_now).to_a
|
||||
pending_posts_daily = Post.where(approval_status: 'pending', created_at: one_day_ago..time_now).to_a
|
||||
end
|
||||
if frequencies_to_notify.include?('weekly') && users_recap_notification_frequencies.include?('weekly')
|
||||
published_posts_weekly = Post.where(approval_status: 'approved', created_at: one_week_ago..time_now).to_a
|
||||
pending_posts_weekly = Post.where(approval_status: 'pending', created_at: one_week_ago..time_now).to_a
|
||||
end
|
||||
if frequencies_to_notify.include?('monthly') && users_recap_notification_frequencies.include?('monthly')
|
||||
published_posts_monthly = Post.where(approval_status: 'approved', created_at: one_month_ago..time_now).to_a
|
||||
pending_posts_monthly = Post.where(approval_status: 'pending', created_at: one_month_ago..time_now).to_a
|
||||
end
|
||||
|
||||
# Notify each user based on their recap notification frequency
|
||||
users.each do |user|
|
||||
# Remove from published_posts the posts published by the user
|
||||
published_posts_daily_user = published_posts_daily&.select { |post| post.user_id != user.id }
|
||||
should_send_daily_recap = published_posts_daily_user&.any? || pending_posts_daily&.any?
|
||||
|
||||
published_posts_weekly_user = published_posts_weekly&.select { |post| post.user_id != user.id }
|
||||
should_send_weekly_recap = published_posts_weekly_user&.any? || pending_posts_weekly&.any?
|
||||
|
||||
published_posts_monthly_user = published_posts_monthly&.select { |post| post.user_id != user.id }
|
||||
should_send_monthly_recap = published_posts_monthly_user&.any? || pending_posts_monthly&.any?
|
||||
|
||||
# Send recap email
|
||||
if user.recap_notification_frequency == 'daily' && should_send_daily_recap
|
||||
UserMailer.recap(
|
||||
frequency: I18n.t('common.forms.auth.recap_notification_frequency_daily'),
|
||||
user: user,
|
||||
published_posts_count: published_posts_daily_user&.count,
|
||||
pending_posts_count: pending_posts_daily&.count,
|
||||
).deliver_later
|
||||
elsif user.recap_notification_frequency == 'weekly' && should_send_weekly_recap
|
||||
UserMailer.recap(
|
||||
frequency: I18n.t('common.forms.auth.recap_notification_frequency_weekly'),
|
||||
user: user,
|
||||
published_posts_count: published_posts_weekly_user&.count,
|
||||
pending_posts_count: pending_posts_weekly&.count,
|
||||
).deliver_later
|
||||
elsif user.recap_notification_frequency == 'monthly' && should_send_monthly_recap
|
||||
UserMailer.recap(
|
||||
frequency: I18n.t('common.forms.auth.recap_notification_frequency_monthly'),
|
||||
user: user,
|
||||
published_posts_count: published_posts_monthly_user&.count,
|
||||
pending_posts_count: pending_posts_monthly&.count,
|
||||
).deliver_later
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -43,6 +43,21 @@ class UserMailer < ApplicationMailer
|
||||
)
|
||||
end
|
||||
|
||||
def recap(frequency:, user:, published_posts_count:, pending_posts_count:)
|
||||
Current.tenant = user.tenant
|
||||
|
||||
@frequency = frequency
|
||||
@user = user
|
||||
@published_posts_count = published_posts_count
|
||||
@pending_posts_count = pending_posts_count
|
||||
|
||||
mail(
|
||||
to: user.email,
|
||||
subject: t('mailers.user.recap.subject', site_name: site_name, frequency: frequency)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def site_name
|
||||
|
||||
@@ -15,6 +15,8 @@ class User < ApplicationRecord
|
||||
enum role: [:user, :moderator, :admin, :owner]
|
||||
enum status: [:active, :blocked, :deleted]
|
||||
|
||||
enum recap_notification_frequency: [:never, :daily, :weekly, :monthly]
|
||||
|
||||
after_initialize :set_default_role, if: :new_record?
|
||||
after_initialize :set_default_status, if: :new_record?
|
||||
|
||||
|
||||
@@ -34,15 +34,6 @@
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :notifications_enabled, t('common.forms.auth.notifications_enabled') %>
|
||||
|
||||
<%= f.check_box :notifications_enabled, style: "transform: scale(1.5)" %>
|
||||
<small id="notificationsHelp" class="form-text text-muted">
|
||||
<%= t('common.forms.auth.notifications_enabled_help') %>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :password, t('common.forms.auth.password') %>
|
||||
<%= f.password_field :password, autocomplete: "new-password", class: "form-control" %>
|
||||
@@ -58,6 +49,39 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<h3><%= t('common.forms.auth.notifications') %></h3>
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<%= f.label :notifications_enabled, t('activerecord.attributes.user.notifications_enabled') %>
|
||||
|
||||
<%= f.check_box :notifications_enabled, style: "transform: scale(1.5)" %>
|
||||
<small id="notificationsHelp" class="form-text text-muted">
|
||||
<%= t('common.forms.auth.notifications_enabled_help') %>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<% if Rails.application.sidekiq_enabled? %>
|
||||
<div class="form-group">
|
||||
<%= f.label :recap_notification_frequency, t('activerecord.attributes.user.recap_notification_frequency') %>
|
||||
<%= f.select :recap_notification_frequency,
|
||||
[
|
||||
[t('common.forms.auth.recap_notification_frequency_never'), "never"],
|
||||
[t('common.forms.auth.recap_notification_frequency_daily'), "daily"],
|
||||
[t('common.forms.auth.recap_notification_frequency_weekly'), "weekly"],
|
||||
[t('common.forms.auth.recap_notification_frequency_monthly'), "monthly"]
|
||||
],
|
||||
{ include_blank: false },
|
||||
class: "form-control" %>
|
||||
<small id="recapNotificationFrequencyHelp" class="form-text text-muted">
|
||||
<%= t('common.forms.auth.recap_notification_frequency_help') %>
|
||||
</small>
|
||||
</div>
|
||||
<% else %>
|
||||
<p>You have to <a href="https://docs.astuto.io/deploy-with-sidekiq">enable Sidekiq</a> to receive recap notifications.</p>
|
||||
<% end %>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="form-group">
|
||||
<%= f.label :current_password, t('common.forms.auth.current_password') %>
|
||||
<%= f.password_field :current_password, autocomplete: "current-password", required: true, class: "form-control" %>
|
||||
|
||||
18
app/views/user_mailer/recap.html.erb
Normal file
18
app/views/user_mailer/recap.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<%= render 'user_mailer/opening', user_name: @user.full_name_or_email %>
|
||||
|
||||
<p>
|
||||
<%= t('mailers.user.recap.body_html', frequency: @frequency.downcase) %>:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><%= t('mailers.user.recap.published_posts_count_html', count: @published_posts_count) %></li>
|
||||
<li><%= t('mailers.user.recap.pending_posts_count_html', count: @pending_posts_count) %></li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<%= link_to t('mailers.user.learn_more'), get_url_for(method(:root_url)) %>
|
||||
</p>
|
||||
|
||||
<%= render 'user_mailer/closing' %>
|
||||
|
||||
<%= render 'user_mailer/unsubscribe_from_site' %>
|
||||
@@ -67,5 +67,9 @@ module App
|
||||
def stripe_yearly_lookup_key
|
||||
ENV["STRIPE_YEARLY_LOOKUP_KEY"]
|
||||
end
|
||||
|
||||
def sidekiq_enabled?
|
||||
ENV["ACTIVE_JOB_BACKEND"] == "sidekiq"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
12
config/initializers/sidekiq_cron.rb
Normal file
12
config/initializers/sidekiq_cron.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
Sidekiq::Cron.configure do |config|
|
||||
config.cron_schedule_file = 'config/sidekiq_cron_schedule.yml'
|
||||
|
||||
config.cron_poll_interval = 30
|
||||
config.cron_history_size = 50
|
||||
config.default_namespace = 'default'
|
||||
config.natural_cron_parsing_mode = :strict
|
||||
|
||||
# Handles the case when the Sidekiq process was down for a while and the cron job should have run (set to 10 minutes, i.e. 600 seconds)
|
||||
# This could happen during the deployment of a new version of the application
|
||||
config.reschedule_grace_period = 600
|
||||
end
|
||||
@@ -50,6 +50,11 @@ en:
|
||||
notify_follower_of_post_status_change:
|
||||
subject: '[%{site_name}] Status change on post "%{post}"'
|
||||
body_html: 'There is a status update on the post you are following <b>%{post}</b>'
|
||||
recap:
|
||||
subject: '[%{site_name}] %{frequency} recap of feedback space activity'
|
||||
body_html: 'Here is the %{frequency} recap of activities in your feedback space'
|
||||
published_posts_count_html: 'New published feedback: %{count}'
|
||||
pending_posts_count_html: 'New feedback pending approval: %{count}'
|
||||
activerecord:
|
||||
models:
|
||||
board:
|
||||
@@ -148,6 +153,7 @@ en:
|
||||
password_confirmation: 'Password confirmation'
|
||||
role: 'Role'
|
||||
notifications_enabled: 'Notifications enabled'
|
||||
recap_notification_frequency: 'Recap notification frequency'
|
||||
errors:
|
||||
messages:
|
||||
invalid: 'is invalid'
|
||||
|
||||
@@ -19,8 +19,13 @@ en:
|
||||
new_password: 'New password'
|
||||
new_password_confirmation: 'New password confirmation'
|
||||
current_password: 'Current password'
|
||||
notifications_enabled: 'Notifications enabled'
|
||||
notifications: 'Notifications'
|
||||
notifications_enabled_help: "if disabled, you won't receive any notification"
|
||||
recap_notification_frequency_never: 'Never'
|
||||
recap_notification_frequency_daily: 'Daily'
|
||||
recap_notification_frequency_weekly: 'Weekly'
|
||||
recap_notification_frequency_monthly: 'Monthly'
|
||||
recap_notification_frequency_help: 'recap notifications let you know if new feedback has been submitted or is waiting for your approval'
|
||||
waiting_confirmation: 'Currently waiting confirmation for %{email}'
|
||||
no_password_set: 'You must set a password to update your profile'
|
||||
set_password: 'Set password'
|
||||
|
||||
9
config/sidekiq_cron_schedule.yml
Normal file
9
config/sidekiq_cron_schedule.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
# For crontab syntax, see https://crontab.guru/
|
||||
|
||||
send_recap_emails:
|
||||
cron: "0 15 * * *" # At 15:00 every day
|
||||
# cron: "*/30 * * * * *" # Execute every 30 seconds (for testing purposes)
|
||||
class: "SendRecapEmails"
|
||||
queue: default
|
||||
args:
|
||||
hour: 15 # This should be in sync with the "cron" time
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddRecapNotificationFrequencyToUsers < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :users, :recap_notification_frequency, :integer, default: 0, null: false
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2024_10_04_170520) do
|
||||
ActiveRecord::Schema.define(version: 2024_11_18_082824) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -239,6 +239,7 @@ ActiveRecord::Schema.define(version: 2024_10_04_170520) do
|
||||
t.datetime "last_sign_in_at"
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.integer "recap_notification_frequency", default: 0, null: false
|
||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||
t.index ["email", "tenant_id"], name: "index_users_on_email_and_tenant_id", unique: true
|
||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
|
||||
187
spec/jobs/send_recap_emails_spec.rb
Normal file
187
spec/jobs/send_recap_emails_spec.rb
Normal file
@@ -0,0 +1,187 @@
|
||||
require 'rails_helper'
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
RSpec.describe SendRecapEmails, type: :job do
|
||||
before do
|
||||
@hour_of_execution = 15
|
||||
|
||||
@admin = FactoryBot.create(:user, role: 'admin', notifications_enabled: true)
|
||||
allow(UserMailer).to receive(:recap).and_call_original
|
||||
end
|
||||
|
||||
it 'sends a daily recap email with published posts count and pending posts count' do
|
||||
@admin.recap_notification_frequency = 'daily'
|
||||
@admin.save
|
||||
|
||||
travel_to Time.now.utc.change(hour: @hour_of_execution) do
|
||||
# Published posts
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 23.hours.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 16.hours.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 30.hours.ago) # Should not be included in recap
|
||||
|
||||
# Pending posts
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 23.hours.ago)
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 30.hours.ago) # Should not be included in recap
|
||||
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
expect(UserMailer).to have_received(:recap).with(
|
||||
frequency: 'Daily',
|
||||
user: @admin,
|
||||
published_posts_count: 2,
|
||||
pending_posts_count: 1
|
||||
).once
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not count posts published by the user receiving the recap email' do
|
||||
@admin.recap_notification_frequency = 'daily'
|
||||
@admin.save
|
||||
|
||||
travel_to Time.now.utc.change(hour: @hour_of_execution) do
|
||||
# Published posts
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 23.hours.ago, user: @admin) # Should not be included in recap
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 16.hours.ago)
|
||||
|
||||
# Pending posts
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 23.hours.ago)
|
||||
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
expect(UserMailer).to have_received(:recap).with(
|
||||
frequency: 'Daily',
|
||||
user: @admin,
|
||||
published_posts_count: 1,
|
||||
pending_posts_count: 1
|
||||
).once
|
||||
end
|
||||
end
|
||||
|
||||
it 'sends a recap email for every owner/admin/mod with notifications enabled and recap_notification_frequency set' do
|
||||
@admin.recap_notification_frequency = 'daily'
|
||||
@admin.save
|
||||
owner = FactoryBot.create(:user, role: 'owner', notifications_enabled: true, recap_notification_frequency: 'daily')
|
||||
mod = FactoryBot.create(:user, role: 'moderator', notifications_enabled: false, recap_notification_frequency: 'daily') # Should not receive recap
|
||||
|
||||
travel_to Time.now.utc.change(hour: @hour_of_execution) do
|
||||
# Published posts
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 23.hours.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 16.hours.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 30.hours.ago) # Should not be included in recap
|
||||
|
||||
# Pending posts
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 23.hours.ago)
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 30.hours.ago) # Should not be included in recap
|
||||
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
expect(UserMailer).to have_received(:recap).with(
|
||||
frequency: 'Daily',
|
||||
user: @admin,
|
||||
published_posts_count: 2,
|
||||
pending_posts_count: 1
|
||||
).once
|
||||
expect(UserMailer).to have_received(:recap).with(
|
||||
frequency: 'Daily',
|
||||
user: owner,
|
||||
published_posts_count: 2,
|
||||
pending_posts_count: 1
|
||||
).once
|
||||
expect(UserMailer).not_to have_received(:recap).with(
|
||||
frequency: 'Daily',
|
||||
user: mod,
|
||||
published_posts_count: 2,
|
||||
pending_posts_count: 1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'sends a weekly recap email with published posts count and pending posts count on Monday' do
|
||||
@admin.recap_notification_frequency = 'weekly'
|
||||
@admin.save
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 18, @hour_of_execution, 0, 0) do # Monday
|
||||
# Published posts
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 6.days.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 10.days.ago) # Should not be included in recap
|
||||
|
||||
# Pending posts
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 1.minute.ago)
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 6.days.ago)
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 10.days.ago) # Should not be included in recap
|
||||
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
expect(UserMailer).to have_received(:recap).with(
|
||||
frequency: 'Weekly',
|
||||
user: @admin,
|
||||
published_posts_count: 2,
|
||||
pending_posts_count: 2
|
||||
).once
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not send a weekly recap email on days other than Monday' do
|
||||
@admin.recap_notification_frequency = 'weekly'
|
||||
@admin.save
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 19, @hour_of_execution, 0, 0) do # Tuesday
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
expect(UserMailer).not_to have_received(:recap)
|
||||
end
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 20, @hour_of_execution, 0, 0) do # Wednesday
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
expect(UserMailer).not_to have_received(:recap)
|
||||
end
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 21, @hour_of_execution, 0, 0) do # Thursday
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
expect(UserMailer).not_to have_received(:recap)
|
||||
end
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 22, @hour_of_execution, 0, 0) do # Friday
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
expect(UserMailer).not_to have_received(:recap)
|
||||
end
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 23, @hour_of_execution, 0, 0) do # Saturday
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
expect(UserMailer).not_to have_received(:recap)
|
||||
end
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 24, @hour_of_execution, 0, 0) do # Sunday
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
expect(UserMailer).not_to have_received(:recap)
|
||||
end
|
||||
end
|
||||
|
||||
it 'sends a monthly recap email with published posts count and pending posts count on the first day of the month' do
|
||||
@admin.recap_notification_frequency = 'monthly'
|
||||
@admin.save
|
||||
|
||||
travel_to Time.zone.local(2024, 11, 1, @hour_of_execution, 0, 0) do # First day of the month
|
||||
# Published posts
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 1.hour.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.days.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 3.weeks.ago)
|
||||
FactoryBot.create(:post, approval_status: 'approved', created_at: 2.months.ago) # Should not be included in recap
|
||||
|
||||
# Pending posts
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 2.days.ago)
|
||||
FactoryBot.create(:post, approval_status: 'pending', created_at: 2.months.ago) # Should not be included in recap
|
||||
|
||||
SendRecapEmails.perform_now(@hour_of_execution)
|
||||
|
||||
expect(UserMailer).to have_received(:recap).with(
|
||||
frequency: 'Monthly',
|
||||
user: @admin,
|
||||
published_posts_count: 3,
|
||||
pending_posts_count: 1
|
||||
).once
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -33,4 +33,13 @@ class UserMailerPreview < ActionMailer::Preview
|
||||
follower = comment.post.follows.first.user
|
||||
UserMailer.notify_follower_of_post_status_change(post: Post.first, follower: follower)
|
||||
end
|
||||
|
||||
def recap
|
||||
UserMailer.recap(
|
||||
frequency: I18n.t('common.forms.auth.recap_notification_frequency_daily'),
|
||||
user: User.first,
|
||||
published_posts_count: 3,
|
||||
pending_posts_count: 2,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user