WIP Refactor/notifications (#1131)
* Add new columns to notifications table * Remove stream from notifications and add new methods * Turn off moderation comment notifications * Add notifiable_type index to migration * Stop creation of Stream-based notification model instances * Add notification model create hooks * Add badge achievement notification creation hook * Fix some specs * Add missing @ symbol * Fix for tests and rename a few things * forgot to uncomment code sigh
This commit is contained in:
parent
faf1c6719b
commit
2db2dd1e5a
25 changed files with 686 additions and 213 deletions
|
|
@ -15,6 +15,7 @@ module Api
|
|||
reactable_type: params[:reactable_type],
|
||||
category: params[:category] || "like",
|
||||
)
|
||||
Notification.send_reaction_notification(@reaction) if @reaction.reactable.user_id != current_user.id
|
||||
render json: { reaction: @reaction.to_json }
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -112,10 +112,10 @@ class ArticlesController < ApplicationController
|
|||
handle_org_assignment
|
||||
handle_hiring_tag
|
||||
if @article.published
|
||||
Notification.send_all(@article, "Published") if @article.previous_changes.include?("published")
|
||||
Notification.send_to_followers(@article, @user.followers, "Published") if @article.saved_changes["published_at"]&.include?(nil)
|
||||
path = @article.path
|
||||
else
|
||||
Notification.remove_all(@article, "Published")
|
||||
Notification.remove_all(id: @article.id, class_name: "Article", action: "Published")
|
||||
path = "/#{@article.username}/#{@article.slug}?preview=#{@article.password}"
|
||||
end
|
||||
redirect_to (params[:destination] || path)
|
||||
|
|
@ -132,6 +132,7 @@ class ArticlesController < ApplicationController
|
|||
def destroy
|
||||
authorize @article
|
||||
@article.destroy!
|
||||
Notification.remove_all(id: @article.id, class_name: "Article", action: "Published")
|
||||
respond_to do |format|
|
||||
format.html { redirect_to "/dashboard", notice: "Article was successfully deleted." }
|
||||
format.json { head :no_content }
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class CommentsController < ApplicationController
|
|||
current_user.update(checked_code_of_conduct: true)
|
||||
end
|
||||
Mention.create_all(@comment)
|
||||
Notification.send_new_comment_notifications(@comment)
|
||||
if @comment.invalid?
|
||||
@comment.destroy
|
||||
render json: { status: "comment already exists" }
|
||||
|
|
@ -115,6 +116,7 @@ class CommentsController < ApplicationController
|
|||
def destroy
|
||||
authorize @comment
|
||||
@commentable_path = @comment.commentable.path
|
||||
Notification.remove_all(id: @comment.id, class_name: "Comment")
|
||||
if @comment.is_childless?
|
||||
@comment.destroy
|
||||
else
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ class FollowsController < ApplicationController
|
|||
User.find(params[:followable_id])
|
||||
end
|
||||
@result = if params[:verb] == "unfollow"
|
||||
current_user.stop_following(followable)
|
||||
follow = current_user.stop_following(followable)
|
||||
Notification.remove_all(id: follow.id, class_name: "Follow")
|
||||
"unfollowed"
|
||||
else
|
||||
current_user.follow(followable)
|
||||
follow = current_user.follow(followable)
|
||||
Notification.send_new_follower_notification(follow)
|
||||
"followed"
|
||||
end
|
||||
current_user.save
|
||||
|
|
|
|||
|
|
@ -41,26 +41,29 @@ class ReactionsController < ApplicationController
|
|||
def create
|
||||
authorize Reaction
|
||||
Rails.cache.delete "count_for_reactable-#{params[:reactable_type]}-#{params[:reactable_id]}"
|
||||
category = params[:category] || "like"
|
||||
reaction = Reaction.where(
|
||||
user_id: current_user.id,
|
||||
reactable_id: params[:reactable_id],
|
||||
reactable_type: params[:reactable_type],
|
||||
category: params[:category] || "like",
|
||||
category: category,
|
||||
).first
|
||||
if reaction
|
||||
reaction.user.touch
|
||||
reaction.destroy
|
||||
Notification.remove_all(id: reaction.id, class_name: "Reaction", action: category) unless reaction.reactable.user_id == current_user.id
|
||||
@result = "destroy"
|
||||
else
|
||||
Reaction.create!(
|
||||
reaction = Reaction.create!(
|
||||
user_id: current_user.id,
|
||||
reactable_id: params[:reactable_id],
|
||||
reactable_type: params[:reactable_type],
|
||||
category: params[:category] || "like",
|
||||
category: category,
|
||||
)
|
||||
Notification.send_reaction_notification(reaction) unless reaction.reactable.user_id == current_user.id
|
||||
@result = "create"
|
||||
end
|
||||
render json: { result: @result, category: params[:category] || "like" }
|
||||
render json: { result: @result, category: category }
|
||||
end
|
||||
|
||||
def cached_user_positive_reactions(user)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class Article < ApplicationRecord
|
|||
has_many :comments, as: :commentable
|
||||
has_many :buffer_updates
|
||||
has_many :reactions, as: :reactable, dependent: :destroy
|
||||
has_many :notifications, as: :notifiable
|
||||
has_many :notifications, as: :notifiable
|
||||
|
||||
validates :slug, presence: { if: :published? }, format: /\A[0-9a-z-]*\z/,
|
||||
uniqueness: { scope: :user_id }
|
||||
|
|
@ -52,6 +52,7 @@ class Article < ApplicationRecord
|
|||
after_save :bust_cache
|
||||
after_save :update_main_image_background_hex
|
||||
after_save :detect_human_language
|
||||
after_update :update_notifications, if: Proc.new { |article| article.notifications.length.positive? && !article.saved_changes.empty? }
|
||||
# after_save :send_to_moderator
|
||||
# turned off for now
|
||||
before_destroy :before_destroy_actions
|
||||
|
|
@ -379,6 +380,10 @@ class Article < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def update_notifications
|
||||
Notification.update_notifications(self, "Published")
|
||||
end
|
||||
|
||||
# def send_to_moderator
|
||||
# ModerationService.new.send_moderation_notification(self) if published
|
||||
# turned off for now
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class BadgeAchievement < ApplicationRecord
|
|||
include StreamRails::Activity
|
||||
as_activity
|
||||
|
||||
after_create :notify_recipient
|
||||
after_create :send_email_notification
|
||||
before_validation :render_rewarding_context_message_html
|
||||
|
||||
|
|
@ -54,6 +55,10 @@ class BadgeAchievement < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def notify_recipient
|
||||
Notification.send_new_badge_notification(self)
|
||||
end
|
||||
|
||||
def send_email_notification
|
||||
if user.class.name == "User" && user.email.present? && user.email_badge_notifications
|
||||
NotifyMailer.new_badge_email(self).deliver
|
||||
|
|
|
|||
|
|
@ -4,28 +4,6 @@ class Broadcast < ApplicationRecord
|
|||
validates :title, :type_of, :processed_html, presence: true
|
||||
validates :type_of, inclusion: { in: %w(Announcement Onboarding) }
|
||||
|
||||
def self.send_welcome_notification(user_id)
|
||||
welcome_broadcast = Broadcast.find_by(title: "Welcome Notification")
|
||||
return if welcome_broadcast == nil
|
||||
Notification.create(
|
||||
user_id: user_id,
|
||||
notifiable_id: welcome_broadcast.id,
|
||||
notifiable_type: "Broadcast",
|
||||
action: welcome_broadcast.type_of,
|
||||
)
|
||||
end
|
||||
|
||||
# method not in use; will be in use if we choose to use markdown
|
||||
def evaluate_markdown
|
||||
return if body_markdown.blank?
|
||||
begin
|
||||
parsed_markdown = MarkdownParser.new(body_markdown)
|
||||
self.processed_html = get_inner_body(parsed_markdown.finalize)
|
||||
rescue StandardError => e
|
||||
errors[:base] << ErrorMessageCleaner.new(e.message).clean
|
||||
end
|
||||
end
|
||||
|
||||
def get_inner_body(content)
|
||||
Nokogiri::HTML(content).at("body").inner_html
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ class Comment < ApplicationRecord
|
|||
after_create :send_to_moderator
|
||||
before_save :set_markdown_character_count
|
||||
before_create :adjust_comment_parent_based_on_depth
|
||||
after_update :update_notifications, if: Proc.new { |comment| comment.saved_changes.include? "body_markdown" }
|
||||
after_update :remove_notifications, if: :deleted
|
||||
before_validation :evaluate_markdown
|
||||
validate :permissions
|
||||
|
||||
|
|
@ -152,7 +154,15 @@ class Comment < ApplicationRecord
|
|||
id.to_s(26)
|
||||
end
|
||||
|
||||
# notifications
|
||||
def custom_css
|
||||
MarkdownParser.new(body_markdown).tags_used.map do |tag|
|
||||
Rails.application.assets["ltags/#{tag}.css"].to_s
|
||||
end.join
|
||||
end
|
||||
|
||||
def title
|
||||
ActionController::Base.helpers.truncate(ActionController::Base.helpers.strip_tags(processed_html), length: 60)
|
||||
end
|
||||
|
||||
def activity_notify
|
||||
user_ids = ancestors.map(&:user_id).to_set
|
||||
|
|
@ -160,12 +170,6 @@ class Comment < ApplicationRecord
|
|||
user_ids.delete(user_id).map { |id| StreamNotifier.new(id).notify }
|
||||
end
|
||||
|
||||
def custom_css
|
||||
MarkdownParser.new(body_markdown).tags_used.map do |tag|
|
||||
Rails.application.assets["ltags/#{tag}.css"].to_s
|
||||
end.join
|
||||
end
|
||||
|
||||
def activity_object
|
||||
self
|
||||
end
|
||||
|
|
@ -189,10 +193,6 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def title
|
||||
ActionController::Base.helpers.truncate(ActionController::Base.helpers.strip_tags(processed_html), length: 60)
|
||||
end
|
||||
|
||||
def video
|
||||
nil
|
||||
end
|
||||
|
|
@ -235,9 +235,17 @@ class Comment < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def update_notifications
|
||||
Notification.update_notifications(self)
|
||||
end
|
||||
|
||||
def remove_notifications
|
||||
Notification.remove_all(id: id, class_name: "Comment")
|
||||
end
|
||||
|
||||
def send_to_moderator
|
||||
return if user && user.comments_count > 10
|
||||
ModerationService.new.send_moderation_notification(self)
|
||||
Notification.send_moderation_notification(self)
|
||||
end
|
||||
|
||||
def evaluate_markdown
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class Mention < ApplicationRecord
|
|||
mentions = []
|
||||
doc.css(".comment-mentioned-user").each do |link|
|
||||
username = link.text.gsub("@", "").downcase
|
||||
if user = User.find_by_username(link.text.gsub("@", "").downcase)
|
||||
if user = User.find_by_username(username)
|
||||
usernames << username
|
||||
mentions << create_mention(user)
|
||||
end
|
||||
|
|
@ -36,12 +36,16 @@ class Mention < ApplicationRecord
|
|||
private
|
||||
|
||||
def delete_removed_mentions(usernames)
|
||||
users = User.where(username: usernames)
|
||||
@notifiable.mentions.where.not(user_id: users.pluck(:id)).destroy_all
|
||||
user_ids = User.where(username: usernames).pluck(:id)
|
||||
mentions = @notifiable.mentions.where.not(user_id: user_ids).destroy_all
|
||||
Notification.remove_each(mentions) unless mentions.blank?
|
||||
end
|
||||
|
||||
def create_mention(user)
|
||||
Mention.create(user_id: user.id, mentionable_id: @notifiable.id, mentionable_type: @notifiable.class.name)
|
||||
mention = Mention.create(user_id: user.id, mentionable_id: @notifiable.id, mentionable_type: @notifiable.class.name)
|
||||
# mentionable_type = model that created the mention, user = user to be mentioned
|
||||
Notification.send_mention_notification(mention)
|
||||
mention
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,77 +2,245 @@ class Notification < ApplicationRecord
|
|||
belongs_to :notifiable, polymorphic: true
|
||||
belongs_to :user
|
||||
|
||||
include StreamRails::Activity
|
||||
as_activity
|
||||
|
||||
validates :user_id, presence: true,
|
||||
uniqueness: { scope: %i[notifiable_id
|
||||
notifiable_type
|
||||
action] }
|
||||
validates :user_id, uniqueness: { scope: %i[notifiable_id notifiable_type action] }
|
||||
|
||||
class << self
|
||||
def send_all(notifiable, action)
|
||||
if notifiable.class.name == "Article"
|
||||
return if notifiable.created_at < Time.new(2017, 0o7, 0o7, 0o0, 0o0, 0o0, "+00:00")
|
||||
notifiable.user.followers.each do |follower|
|
||||
Notification.create(
|
||||
user_id: follower.id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: "Article",
|
||||
action: action,
|
||||
)
|
||||
end
|
||||
elsif notifiable.class.name == "Broadcast"
|
||||
if action == "Announcement"
|
||||
User.all.each do |user|
|
||||
Notification.create!(
|
||||
user_id: user.id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: "Broadcast",
|
||||
action: action,
|
||||
)
|
||||
end
|
||||
end
|
||||
def send_new_follower_notification(follow)
|
||||
json_data = { user: user_data(follow.follower) }
|
||||
Notification.create(
|
||||
user_id: follow.followable.id,
|
||||
notifiable_id: follow.id,
|
||||
notifiable_type: follow.class.name,
|
||||
action: nil,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
handle_asynchronously :send_new_follower_notification
|
||||
|
||||
def send_to_followers(notifiable, followers, action = nil)
|
||||
# followers is an array and not an activerecord object
|
||||
followers.sort_by(&:updated_at).reverse[0..2500].each do |follower|
|
||||
json_data = {
|
||||
user: user_data(notifiable.user),
|
||||
article: article_data(notifiable)
|
||||
}
|
||||
Notification.create(
|
||||
user_id: follower.id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: notifiable.class.name,
|
||||
action: action,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
end
|
||||
handle_asynchronously :send_all
|
||||
handle_asynchronously :send_to_followers
|
||||
|
||||
def remove_all(notifiable, action)
|
||||
Notification.where(
|
||||
def send_new_comment_notifications(notifiable)
|
||||
user_ids = notifiable.ancestors.map(&:user_id).to_set
|
||||
user_ids.add(notifiable.commentable.user.id) if user_ids.empty?
|
||||
user_ids.delete(notifiable.user_id).each do |user_id|
|
||||
json_data = {
|
||||
user: user_data(notifiable.user),
|
||||
comment: comment_data(notifiable)
|
||||
}
|
||||
Notification.create(
|
||||
user_id: user_id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: notifiable.class.name,
|
||||
action: nil,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
end
|
||||
handle_asynchronously :send_new_comment_notifications
|
||||
|
||||
def send_new_badge_notification(notifiable)
|
||||
json_data = {
|
||||
user: user_data(notifiable.user),
|
||||
badge_achievement: {
|
||||
badge_id: notifiable.badge_id,
|
||||
rewarding_context_message: notifiable.rewarding_context_message,
|
||||
badge: {
|
||||
title: notifiable.badge.title,
|
||||
description: notifiable.badge.description,
|
||||
badge_image_url: notifiable.badge.badge_image_url
|
||||
}
|
||||
}
|
||||
}
|
||||
Notification.create(
|
||||
user_id: notifiable.user.id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: "BadgeAchievement",
|
||||
action: nil,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
handle_asynchronously :send_new_badge_notification
|
||||
|
||||
def send_reaction_notification(notifiable)
|
||||
json_data = {
|
||||
user: user_data(notifiable.user),
|
||||
reaction: {
|
||||
category: notifiable.category,
|
||||
reactable_type: notifiable.reactable_type,
|
||||
reactable_id: notifiable.reactable_id,
|
||||
reactable: {
|
||||
path: notifiable.reactable.path,
|
||||
title: notifiable.reactable.title
|
||||
},
|
||||
updated_at: notifiable.updated_at
|
||||
}
|
||||
}
|
||||
Notification.create(
|
||||
user_id: notifiable.reactable.user.id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: "Reaction",
|
||||
action: notifiable.category,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
handle_asynchronously :send_reaction_notification
|
||||
|
||||
def send_mention_notification(notifiable)
|
||||
mentioner = notifiable.mentionable.user
|
||||
json_data = {
|
||||
user: user_data(mentioner)
|
||||
}
|
||||
json_data[:comment] = comment_data(notifiable.mentionable) if notifiable.mentionable_type == "Comment"
|
||||
Notification.create(
|
||||
user_id: notifiable.user_id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: "Mention",
|
||||
action: nil,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
handle_asynchronously :send_mention_notification
|
||||
|
||||
def send_welcome_notification(receiver_id)
|
||||
welcome_broadcast = Broadcast.find_by(title: "Welcome Notification")
|
||||
return if welcome_broadcast == nil
|
||||
dev_account = User.find_by_id(ENV["DEVTO_USER_ID"])
|
||||
json_data = {
|
||||
user: user_data(dev_account),
|
||||
broadcast: {
|
||||
processed_html: welcome_broadcast.processed_html
|
||||
}
|
||||
}
|
||||
Notification.create(
|
||||
user_id: receiver_id,
|
||||
notifiable_id: welcome_broadcast.id,
|
||||
notifiable_type: "Broadcast",
|
||||
action: welcome_broadcast.type_of,
|
||||
json_data: json_data,
|
||||
)
|
||||
end
|
||||
handle_asynchronously :send_welcome_notification
|
||||
|
||||
def send_moderation_notification(notifiable)
|
||||
available_moderators = User.with_role(:trusted).where("last_moderation_notification < ?", 28.hours.ago)
|
||||
return if available_moderators.empty?
|
||||
moderator = available_moderators.sample
|
||||
dev_account = User.find_by_id(ENV["DEVTO_USER_ID"])
|
||||
json_data = {
|
||||
user: user_data(dev_account)
|
||||
}
|
||||
json_data[notifiable.class.name.downcase] = send "#{notifiable.class.name.downcase}_data", notifiable
|
||||
Notification.create(
|
||||
user_id: moderator.id,
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: notifiable.class.name,
|
||||
action: action,
|
||||
action: "Moderation",
|
||||
json_data: json_data,
|
||||
)
|
||||
moderator.update_column(:last_moderation_notification, Time.current)
|
||||
end
|
||||
handle_asynchronously :send_moderation_notification
|
||||
|
||||
def remove_all(notifiable_hash)
|
||||
Notification.where(
|
||||
notifiable_id: notifiable_hash[:id],
|
||||
notifiable_type: notifiable_hash[:class_name],
|
||||
action: notifiable_hash[:action],
|
||||
).destroy_all
|
||||
end
|
||||
handle_asynchronously :remove_all
|
||||
end
|
||||
|
||||
def activity_actor
|
||||
if notifiable.class.name == "Broadcast" || action == "Moderation"
|
||||
User.find(ApplicationConfig["DEVTO_USER_ID"])
|
||||
else
|
||||
notifiable&.user
|
||||
def remove_each(notifiable_collection, action = nil)
|
||||
# only used for mentions since it's an array
|
||||
notifiable_collection.each do |notifiable|
|
||||
Notification.where(
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: notifiable.class.name,
|
||||
action: action,
|
||||
).destroy_all
|
||||
end
|
||||
end
|
||||
handle_asynchronously :remove_each
|
||||
|
||||
def update_notifications(notifiable, action = nil)
|
||||
notifications = Notification.where(
|
||||
notifiable_id: notifiable.id,
|
||||
notifiable_type: notifiable.class.name,
|
||||
action: action,
|
||||
)
|
||||
return if notifications.blank?
|
||||
new_json_data = notifications.first.json_data
|
||||
new_json_data[notifiable.class.name.downcase] = send("#{notifiable.class.name.downcase}_data", notifiable)
|
||||
notifications.update_all(json_data: new_json_data)
|
||||
end
|
||||
handle_asynchronously :update_notifications
|
||||
|
||||
private
|
||||
|
||||
def user_data(user)
|
||||
{
|
||||
id: user.id,
|
||||
class: { name: "User" },
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
path: user.path,
|
||||
profile_image_90: user.profile_image_90
|
||||
}
|
||||
end
|
||||
|
||||
def comment_data(comment)
|
||||
{
|
||||
id: comment.id,
|
||||
class: { name: "Comment" },
|
||||
path: comment.path,
|
||||
processed_html: comment.processed_html,
|
||||
updated_at: comment.updated_at,
|
||||
commentable: {
|
||||
id: comment.commentable.id,
|
||||
title: comment.commentable.title,
|
||||
path: comment.commentable.path,
|
||||
class: {
|
||||
name: comment.commentable.class.name
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def article_data(article)
|
||||
{
|
||||
id: article.id,
|
||||
cached_tag_list_array: article.decorate.cached_tag_list_array,
|
||||
class: { name: "Article" },
|
||||
title: article.title,
|
||||
path: article.path,
|
||||
updated_at: article.updated_at
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def activity_object
|
||||
notifiable
|
||||
end
|
||||
# instance methods
|
||||
|
||||
def activity_verb
|
||||
"#{notifiable_type}_#{action}"
|
||||
end
|
||||
|
||||
def activity_target
|
||||
"#{notifiable_type.downcase}_#{Time.current.utc}"
|
||||
end
|
||||
|
||||
def activity_notify
|
||||
[StreamNotifier.new(user_id).notify]
|
||||
end
|
||||
|
||||
def remove_from_feed
|
||||
super
|
||||
User.find_by(id: user_id)&.touch(:last_notification_activity)
|
||||
def aggregation_format
|
||||
if notifiable_type == "Reaction"
|
||||
"#{created_at.beginning_of_day}-#{created_at.end_of_day}_#{json_data['reaction']['reactable_id']}_#{json_data['reaction']['reactable_type']}"
|
||||
elsif notifiable_type == "Follow"
|
||||
"#{created_at.beginning_of_day}-#{created_at.end_of_day}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ class User < ApplicationRecord
|
|||
private
|
||||
|
||||
def send_welcome_notification
|
||||
Broadcast.send_welcome_notification(id)
|
||||
Notification.send_welcome_notification(id)
|
||||
end
|
||||
|
||||
def set_username
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class ArticleCreationService
|
|||
create_job_opportunity(article)
|
||||
if article.save
|
||||
if article.published
|
||||
Notification.send_all(article, "Published")
|
||||
Notification.send_to_followers(article, user.followers, "Published")
|
||||
end
|
||||
end
|
||||
article.decorate
|
||||
|
|
|
|||
10
db/migrate/20181026214021_add_columns_to_notifications.rb
Normal file
10
db/migrate/20181026214021_add_columns_to_notifications.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
class AddColumnsToNotifications < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_index :notifications, :notifiable_id
|
||||
add_index :notifications, :user_id
|
||||
add_index :notifications, :notifiable_type
|
||||
add_column :notifications, :json_data, :jsonb
|
||||
add_index :notifications, :json_data, using: :gin
|
||||
add_column :notifications, :read?, :boolean, default: false
|
||||
end
|
||||
end
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This file is auto-generated from the current state of the database. Instead
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
|
|
@ -454,10 +456,16 @@ ActiveRecord::Schema.define(version: 20181116223239) do
|
|||
create_table "notifications", id: :serial, force: :cascade do |t|
|
||||
t.string "action"
|
||||
t.datetime "created_at", null: false
|
||||
t.jsonb "json_data"
|
||||
t.integer "notifiable_id"
|
||||
t.string "notifiable_type"
|
||||
t.boolean "read?", default: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "user_id"
|
||||
t.index ["json_data"], name: "index_notifications_on_json_data", using: :gin
|
||||
t.index ["notifiable_id"], name: "index_notifications_on_notifiable_id"
|
||||
t.index ["notifiable_type"], name: "index_notifications_on_notifiable_type"
|
||||
t.index ["user_id"], name: "index_notifications_on_user_id"
|
||||
end
|
||||
|
||||
create_table "organizations", id: :serial, force: :cascade do |t|
|
||||
|
|
|
|||
|
|
@ -34,4 +34,12 @@ FactoryBot.define do
|
|||
HEREDOC
|
||||
end
|
||||
end
|
||||
|
||||
trait :video do
|
||||
after(:build) do |article|
|
||||
article.video = "https://video.com"
|
||||
article.user.add_role :video_permission
|
||||
article.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
# rubocop:disable RSpec/InstanceVariable
|
||||
require "rails_helper"
|
||||
|
||||
describe "Send a broadcast" do
|
||||
describe "Onboarding/welcome broadcast" do
|
||||
describe "sent broadcast" do
|
||||
before do
|
||||
@broadcast = FactoryBot.create(:broadcast, :onboarding, :sent)
|
||||
@new_user = FactoryBot.create(:user)
|
||||
@welcome_notification = Broadcast.send_welcome_notification(@new_user.id)
|
||||
end
|
||||
|
||||
it "has a welcome notification" do
|
||||
expect(@welcome_notification.notifiable).to eq @broadcast
|
||||
end
|
||||
|
||||
it "is properly sent to the new user" do
|
||||
expect(@welcome_notification.user).to eq @new_user
|
||||
end
|
||||
|
||||
it "has a link to the welcome thread" do
|
||||
expect(@broadcast.processed_html.html_safe).to include("/welcome")
|
||||
end
|
||||
end
|
||||
|
||||
describe "unsent broadcast" do
|
||||
before do
|
||||
FactoryBot.create(:broadcast, :onboarding)
|
||||
end
|
||||
|
||||
it "is an unsent broadcast that doesn't create a notification" do
|
||||
expect(Notification.all.empty?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable RSpec/InstanceVariable
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Deleting Article", js: true do
|
||||
let(:author) { create(:user) }
|
||||
let(:article) { create(:article, user_id: author.id) }
|
||||
let(:author) { create(:user) }
|
||||
let(:article) { create(:article, user_id: author.id) }
|
||||
|
||||
def delete_article_via_dashboard
|
||||
visit "/dashboard"
|
||||
|
|
@ -13,7 +13,7 @@ RSpec.describe "Deleting Article", js: true do
|
|||
end
|
||||
|
||||
before do
|
||||
article
|
||||
Notification.send_to_followers(article, [author], "Published")
|
||||
end
|
||||
|
||||
it "author of article deletes own article" do
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ RSpec.describe "Editing A Comment", type: :feature, js: true do
|
|||
let(:new_comment_text) { Faker::Lorem.paragraph }
|
||||
|
||||
before do
|
||||
create(:comment, commentable: article, user: user, body_markdown: Faker::Lorem.paragraph)
|
||||
comment = create(:comment, commentable: article, user: user, body_markdown: Faker::Lorem.paragraph)
|
||||
Notification.send_new_comment_notifications(comment)
|
||||
sign_in user
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,19 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Comment, type: :model do
|
||||
let(:user) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id, published: true) }
|
||||
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
|
||||
let(:comment_2) { create(:comment, user_id: user.id, commentable_id: article.id) }
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id, published: true) }
|
||||
let(:article_with_video) { create(:article, :video, user_id: user.id, published: true) } # :video is a trait, see articles.rb
|
||||
let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id) }
|
||||
let(:video_comment) { create(:comment, user_id: user2.id, commentable_id: article_with_video.id) }
|
||||
let(:comment_2) { create(:comment, user_id: user2.id, commentable_id: article.id) }
|
||||
let(:child_comment) do
|
||||
build(:comment, user_id: user.id, commentable_id: article.id, parent_id: comment.id)
|
||||
end
|
||||
|
||||
before { Notification.send_new_comment_notifications(comment) }
|
||||
|
||||
it "gets proper generated ID code" do
|
||||
expect(comment.id_code_generated).to eq(comment.id.to_s(26))
|
||||
end
|
||||
|
|
@ -18,8 +23,9 @@ RSpec.describe Comment, type: :model do
|
|||
expect(comment.markdown_character_count).to eq(comment.body_markdown.size)
|
||||
end
|
||||
|
||||
context "when comment is already posted " do
|
||||
context "when comment is already posted" do
|
||||
before do
|
||||
Notification.send_new_comment_notifications(comment_2)
|
||||
comment_2.update(ancestry: comment.ancestry,
|
||||
body_markdown: comment.body_markdown,
|
||||
commentable_type: comment.commentable_type,
|
||||
|
|
@ -49,25 +55,23 @@ RSpec.describe Comment, type: :model do
|
|||
end
|
||||
|
||||
it "adds timestamp url if commentable has video and timestamp" do
|
||||
article.video = "https://video.com"
|
||||
article.user.add_role(:video_permission)
|
||||
article.save!
|
||||
comment.body_markdown = "I like the part at 4:30"
|
||||
comment.save
|
||||
expect(comment.processed_html.include?(">4:30</a>")).to eq(true)
|
||||
comment.body_markdown = "I like the part at 4:30 and 5:50"
|
||||
comment.save
|
||||
expect(comment.processed_html.include?(">5:50</a>")).to eq(true)
|
||||
comment.body_markdown = "I like the part at 5:30 and :55"
|
||||
comment.save
|
||||
expect(comment.processed_html.include?(">:55</a>")).to eq(true)
|
||||
comment.body_markdown = "I like the part at 52:30"
|
||||
comment.save
|
||||
expect(comment.processed_html.include?(">52:30</a>")).to eq(true)
|
||||
comment.body_markdown = "I like the part at 1:52:30 and 1:20"
|
||||
comment.save
|
||||
expect(comment.processed_html.include?(">1:52:30</a>")).to eq(true)
|
||||
expect(comment.processed_html.include?(">1:20</a>")).to eq(true)
|
||||
Notification.send_new_comment_notifications(video_comment)
|
||||
video_comment.body_markdown = "I like the part at 4:30"
|
||||
video_comment.save
|
||||
expect(video_comment.processed_html.include?(">4:30</a>")).to eq(true)
|
||||
video_comment.body_markdown = "I like the part at 4:30 and 5:50"
|
||||
video_comment.save
|
||||
expect(video_comment.processed_html.include?(">5:50</a>")).to eq(true)
|
||||
video_comment.body_markdown = "I like the part at 5:30 and :55"
|
||||
video_comment.save
|
||||
expect(video_comment.processed_html.include?(">:55</a>")).to eq(true)
|
||||
video_comment.body_markdown = "I like the part at 52:30"
|
||||
video_comment.save
|
||||
expect(video_comment.processed_html.include?(">52:30</a>")).to eq(true)
|
||||
video_comment.body_markdown = "I like the part at 1:52:30 and 1:20"
|
||||
video_comment.save
|
||||
expect(video_comment.processed_html.include?(">1:52:30</a>")).to eq(true)
|
||||
expect(video_comment.processed_html.include?(">1:20</a>")).to eq(true)
|
||||
end
|
||||
|
||||
it "does not add timestamp if commentable does not have video" do
|
||||
|
|
@ -163,7 +167,7 @@ RSpec.describe Comment, type: :model do
|
|||
end
|
||||
|
||||
it "returns the root parent comment's user if root parent comment exists" do
|
||||
expect(child_comment.parent_user).to eq(user)
|
||||
expect(child_comment.parent_user).to eq(user2)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ RSpec.describe Mention, type: :model do
|
|||
)
|
||||
end
|
||||
|
||||
before { Notification.send_new_comment_notifications(comment) }
|
||||
|
||||
it "creates mention if there is a user mentioned" do
|
||||
comment.body_markdown = "Hello @#{user.username}, you are cool."
|
||||
comment.save
|
||||
|
|
|
|||
|
|
@ -1,35 +1,30 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Notification, type: :model do
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:user3) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:user3) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:follow_instance) { user.follow(user2) }
|
||||
|
||||
it "sends notifications to all followers of article user" do
|
||||
user2.follow(user)
|
||||
user3.follow(user)
|
||||
Notification.send_all_without_delay(article, "Published")
|
||||
expect(Notification.all.size).to eq(2)
|
||||
describe "#send_new_follower_notification" do
|
||||
before { Notification.send_new_follower_notification(follow_instance) }
|
||||
|
||||
it "creates a notification belonging to the person being followed" do
|
||||
expect(Notification.first.user_id).to eq user2.id
|
||||
end
|
||||
|
||||
it "creates a notification from the follow instance" do
|
||||
notifiable_data = { notifiable_id: Notification.first.notifiable_id, notifiable_type: Notification.first.notifiable_type }
|
||||
follow_data = { notifiable_id: follow_instance.id, notifiable_type: follow_instance.class.name }
|
||||
expect(notifiable_data).to eq follow_data
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable RSpec/ExampleLength
|
||||
it "sends sends a broadcast to everyone" do
|
||||
user2.follow(user)
|
||||
user3.follow(user)
|
||||
broadcast = Broadcast.create!(
|
||||
processed_html: "Hello", title: "test broadcast", type_of: "Announcement",
|
||||
)
|
||||
Notification.send_all_without_delay(broadcast, "Announcement")
|
||||
expect(Notification.all.size).to eq(3)
|
||||
end
|
||||
# rubocop:enable RSpec/ExampleLength
|
||||
|
||||
it "removes all notifications" do
|
||||
user2.follow(user)
|
||||
user3.follow(user)
|
||||
Notification.send_all_without_delay(article, "Published")
|
||||
Notification.remove_all_without_delay(article, "Published")
|
||||
expect(Notification.all.size).to eq(0)
|
||||
end
|
||||
# describe "#send_to_followers" do
|
||||
# before do
|
||||
# user2.follow user
|
||||
# Notification.send_to_followers(article, user.followers, "Published")
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ RSpec.describe "CommentsUpdate", type: :request do
|
|||
|
||||
before do
|
||||
sign_in user
|
||||
Notification.send_new_comment_notifications(comment)
|
||||
end
|
||||
|
||||
it "updates ordinary article with proper params" do
|
||||
|
|
|
|||
|
|
@ -1,21 +1,325 @@
|
|||
require "rails_helper"
|
||||
|
||||
# vcr_option = {
|
||||
# cassette_name: "getstream-index",
|
||||
# allow_playback_repeats: "true",
|
||||
# }
|
||||
|
||||
RSpec.describe "NotificationsIndex", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe "GET logged-in notifications index" do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
it "renders page with proper sidebar" do
|
||||
describe "GET notifications" do
|
||||
xit "renders page with the proper heading" do
|
||||
get "/notifications"
|
||||
expect(response.body).to include("Notifications")
|
||||
end
|
||||
|
||||
context "when signed out" do
|
||||
xit "renders the signup cue" do
|
||||
get "/notifications"
|
||||
expect(response.body).to include "<div class=\"signup-cue"
|
||||
end
|
||||
end
|
||||
|
||||
context "when signed in" do
|
||||
before { sign_in user }
|
||||
|
||||
xit "does not render the signup cue" do
|
||||
get "/notifications"
|
||||
expect(response.body).not_to include "Create your account"
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has new follow notifications" do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
def mock_follow_notifications(amount)
|
||||
create_list :user, amount
|
||||
follow_instances = User.last(amount).map { |follower| follower.follow(user) }
|
||||
follow_instances.each { |follow| Notification.send_new_follower_notification(follow) }
|
||||
end
|
||||
|
||||
xit "renders the proper message for a single notification" do
|
||||
mock_follow_notifications(1)
|
||||
get "/notifications"
|
||||
follow_message = "#{User.last.name}</a> followed you!"
|
||||
expect(response.body).to include follow_message
|
||||
end
|
||||
|
||||
xit "renders the proper message for two notifications in the same day" do
|
||||
mock_follow_notifications(2)
|
||||
get "/notifications"
|
||||
follow_message = "#{User.last.name}</a> and\n <a href=\"/#{User.second_to_last.username}\">#{User.second_to_last.name}</a> followed you!"
|
||||
expect(response.body).to include follow_message
|
||||
end
|
||||
|
||||
xit "renders the proper message for three or more notifications in the same day" do
|
||||
mock_follow_notifications(rand(3..10))
|
||||
get "/notifications"
|
||||
follow_message = "others followed you!"
|
||||
expect(response.body).to include follow_message
|
||||
end
|
||||
|
||||
xit "groups two notifications on the same day" do
|
||||
mock_follow_notifications(2)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
# only one notification object containing a group of notifications
|
||||
expect(notifications.count).to eq 1
|
||||
end
|
||||
|
||||
xit "groups three or more notifications on the same day" do
|
||||
amount = rand(3..10)
|
||||
mock_follow_notifications(amount)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 1
|
||||
end
|
||||
|
||||
xit "does not group notifications that occur on different days" do
|
||||
mock_follow_notifications(2)
|
||||
Notification.last.update(created_at: Notification.last.created_at - 1.day)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has new reaction notifications" do
|
||||
let(:article1) { create(:article, user_id: user.id) }
|
||||
let(:article2) { create(:article, user_id: user.id) }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
def mock_heart_reaction_notifications(amount, categories, reactable = article1)
|
||||
create_list :user, amount
|
||||
reactions = User.last(amount).map do |user|
|
||||
create(
|
||||
:reaction,
|
||||
user_id: user.id,
|
||||
reactable_id: reactable.id,
|
||||
reactable_type: reactable.class.name,
|
||||
category: categories.sample,
|
||||
)
|
||||
end
|
||||
reactions.each { |reaction| Notification.send_reaction_notification(reaction) }
|
||||
end
|
||||
|
||||
xit "renders the proper message for a single public reaction" do
|
||||
mock_heart_reaction_notifications(1, %w(like unicorn))
|
||||
get "/notifications"
|
||||
message = "#{User.last.name}</strong></a> reacted to"
|
||||
expect(response.body).to include message
|
||||
end
|
||||
|
||||
xit "renders the proper message for a single private reaction" do
|
||||
mock_heart_reaction_notifications(1, %w(readinglist))
|
||||
get "/notifications"
|
||||
message = "Someone reacted to"
|
||||
expect(response.body).to include message
|
||||
end
|
||||
|
||||
xit "renders the proper message for two or more public reactions" do
|
||||
mock_heart_reaction_notifications(2, %w(like unicorn))
|
||||
get "/notifications"
|
||||
message = "#{User.last.name}</a> and <a href=\"/#{User.second_to_last.username}\">#{User.second_to_last.name}</a>\n reacted to"
|
||||
expect(response.body).to include message
|
||||
end
|
||||
|
||||
xit "renders the proper message for two or more reactions where at least one is private" do
|
||||
mock_heart_reaction_notifications(1, %w(readinglist))
|
||||
mock_heart_reaction_notifications(1, %w(unicorn like))
|
||||
get "/notifications"
|
||||
message = "Devs\n reacted to"
|
||||
expect(response.body).to include message
|
||||
end
|
||||
|
||||
xit "renders the proper message for multiple public reactions" do
|
||||
mock_heart_reaction_notifications(3, %w(unicorn like))
|
||||
get "/notifications"
|
||||
message = "#{User.last.name}</a> and 2 others\n reacted to"
|
||||
expect(response.body).to include message
|
||||
end
|
||||
|
||||
xit "properly groups two notifications that have the same day and reactable" do
|
||||
mock_heart_reaction_notifications(2, %w(unicorn like readinglist))
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 1
|
||||
end
|
||||
|
||||
xit "properly groups three or more notifications that have the same day and reactable" do
|
||||
amount = rand(3..10)
|
||||
mock_heart_reaction_notifications(amount, %w(unicorn like readinglist))
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 1
|
||||
end
|
||||
|
||||
xit "does not group notifications that are on different days but same reactable" do
|
||||
mock_heart_reaction_notifications(2, %w(unicorn like readinglist))
|
||||
Notification.last.update(created_at: Notification.last.created_at - 1.day)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 2
|
||||
end
|
||||
|
||||
xit "does not group notifications that are on the same day but different reactables" do
|
||||
mock_heart_reaction_notifications(1, %w(unicorn like readinglist), article1)
|
||||
mock_heart_reaction_notifications(1, %w(unicorn like readinglist), article2)
|
||||
get "/notifications"
|
||||
notifications = controller.instance_variable_get(:@notifications)
|
||||
expect(notifications.count).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has a new comment notification" do
|
||||
let(:user2) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") }
|
||||
|
||||
before do
|
||||
sign_in user
|
||||
Notification.send_new_comment_notifications(comment)
|
||||
get "/notifications"
|
||||
end
|
||||
|
||||
xit "renders the correct message" do
|
||||
expect(response.body).to include "commented on"
|
||||
end
|
||||
|
||||
xit "does not render the moderation message" do
|
||||
expect(response.body).not_to include "As a trusted member"
|
||||
end
|
||||
|
||||
xit "renders the original article's title" do
|
||||
expect(response.body).to include article.title
|
||||
end
|
||||
|
||||
xit "renders the comment's processed HTML" do
|
||||
expect(response.body).to include comment.processed_html
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has a new moderation notification" do
|
||||
let(:user2) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") }
|
||||
|
||||
before do
|
||||
user.update(id: 1)
|
||||
user.add_role :trusted
|
||||
sign_in user
|
||||
Notification.send_moderation_notification(comment)
|
||||
get "/notifications"
|
||||
end
|
||||
|
||||
xit "renders the proper message" do
|
||||
expect(response.body).to include "As a trusted member"
|
||||
end
|
||||
|
||||
xit "renders the article's title" do
|
||||
expect(response.body).to include article.title
|
||||
end
|
||||
|
||||
xit "renders the comment's processed HTML" do
|
||||
expect(response.body).to include comment.processed_html
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has a new welcome notification" do
|
||||
before do
|
||||
user.update(id: 1)
|
||||
sign_in user
|
||||
end
|
||||
|
||||
xit "renders the welcome notification" do
|
||||
broadcast = create(:broadcast, :onboarding)
|
||||
Notification.send_welcome_notification(user.id)
|
||||
get "/notifications"
|
||||
expect(response.body).to include broadcast.processed_html
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has a new badge notification" do
|
||||
before do
|
||||
sign_in user
|
||||
badge = create(:badge)
|
||||
badge_achievement = create(:badge_achievement, user: user, badge: badge)
|
||||
Notification.send_new_badge_notification(badge_achievement)
|
||||
get "/notifications"
|
||||
end
|
||||
|
||||
xit "renders the proper message with the badge's title" do
|
||||
message = "You received the <strong>#{Badge.first.title}"
|
||||
expect(response.body).to include message
|
||||
end
|
||||
|
||||
xit "renders the rewarding context message" do
|
||||
expect(response.body).to include user.badge_achievements.first.rewarding_context_message
|
||||
end
|
||||
|
||||
xit "renders the badge's description" do
|
||||
expect(response.body).to include Badge.first.description
|
||||
end
|
||||
|
||||
xit "renders the CHECK YOUR PROFILE button" do
|
||||
expect(response.body).to include "CHECK YOUR PROFILE"
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has a new mention notification" do
|
||||
let(:user2) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:comment) do
|
||||
create(
|
||||
:comment,
|
||||
user_id: user2.id,
|
||||
commentable_id: article.id,
|
||||
commentable_type: "Article",
|
||||
body_markdown: "@#{user.username}",
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
user.update(id: 1)
|
||||
sign_in user
|
||||
comment
|
||||
Mention.create_all(comment)
|
||||
Notification.send_mention_notification(Mention.first)
|
||||
get "/notifications"
|
||||
end
|
||||
|
||||
xit "renders the proper message" do
|
||||
expect(response.body).to include "mentioned you in a comment"
|
||||
end
|
||||
|
||||
xit "renders the processed HTML of the comment where they were mentioned" do
|
||||
expect(response.body).to include comment.processed_html
|
||||
end
|
||||
end
|
||||
|
||||
context "when a user has a new article notification" do
|
||||
let(:user2) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
|
||||
before do
|
||||
user2.follow(user)
|
||||
Notification.send_to_followers(article, article.user.followers, "Published")
|
||||
sign_in user2
|
||||
get "/notifications"
|
||||
end
|
||||
|
||||
xit "renders the proper message" do
|
||||
expect(response.body).to include "made a new post:"
|
||||
end
|
||||
|
||||
xit "renders the article's title" do
|
||||
expect(response.body).to include article.title
|
||||
end
|
||||
|
||||
xit "renders the author's name" do
|
||||
expect(response.body).to include article.user.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "ArticlesApi", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
let(:user) { create(:user, :super_admin) }
|
||||
let(:article) { create(:article) }
|
||||
|
||||
before do
|
||||
user.update(secret: "TEST_SECRET")
|
||||
user.add_role(:super_admin)
|
||||
sign_in user
|
||||
end
|
||||
|
||||
describe "POST /api/reactions" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue