require "rails_helper" RSpec.describe "NotificationsIndex", type: :request do include ActionView::Helpers::DateHelper let(:dev_account) { create(:user) } let(:user) { create(:user) } before do allow(User).to receive(:dev_account).and_return(dev_account) end def has_both_names(response_body) response_body.include?(CGI.escapeHTML(User.last.name)) && response_body.include?(CGI.escapeHTML(User.second_to_last.name)) end describe "GET /notifications" do it "renders page with the proper heading" do get "/notifications" expect(response.body).to include("Notifications") end context "when signed out" do it "renders the signup cue" do get "/notifications" expect(response.body).to include "Great to have you" end end context "when signed in" do it "does not render the signup cue" do sign_in user get "/notifications" expect(response.body).not_to include "Create your account" end end context "when a user has new follow notifications" do before { sign_in user } 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_without_delay(follow) } end it "renders the proper message for a single notification" do mock_follow_notifications(1) get "/notifications" expect(response.body).to include CGI.escapeHTML(User.last.name) end it "renders the proper message for two notifications in the same day" do mock_follow_notifications(2) get "/notifications" expect(has_both_names(response.body)).to be true end it "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 it "does 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 1 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) } let(:special_characters_article) { create(:article, user_id: user.id, title: "What's Become of Waring") } before { sign_in user } 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_without_delay(reaction, reaction.reactable.user) } end it "renders the correct user for a single reaction" do mock_heart_reaction_notifications(1, %w[like unicorn]) get "/notifications" expect(response.body).to include CGI.escapeHTML(User.last.name) end it "renders the correct usernames for two or more reactions" do mock_heart_reaction_notifications(2, %w[like unicorn]) get "/notifications" expect(has_both_names(response.body)).to be true end it "renders the proper message for multiple reactions" do random_amount = rand(3..10) mock_heart_reaction_notifications(random_amount, %w[unicorn like]) get "/notifications" expect(response.body).to include CGI.escapeHTML("and #{random_amount - 1} others") end it "does group notifications that are on different days but have the 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 1 end it "does not group notifications that are on the same day but have 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 it "properly renders reactable titles" do mock_heart_reaction_notifications(1, %w[unicorn like readinglist], special_characters_article) get "/notifications" expect(response.body).to include ERB::Util.html_escape(special_characters_article.title) end it "properly renders reactable titles for multiple reactions" do amount = rand(3..10) mock_heart_reaction_notifications(amount, %w[unicorn like readinglist], special_characters_article) get "/notifications" expect(response.body).to include ERB::Util.html_escape(special_characters_article.title) end end context "when a user has a new comment notification" do let(:user2) { create(:user) } let(:article) { create(:article, :with_notification_subscription, 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_without_delay(comment) get "/notifications" end it "renders the correct message" do expect(response.body).to include "commented on" end it "does not render incorrect message" do expect(response.body).not_to include "replied to a thread in" end it "does not render the moderation message" do expect(response.body).not_to include "As a trusted member" end it "renders the article's path" do expect(response.body).to include article.path end it "renders the comment's processed HTML" do expect(response.body).to include comment.processed_html end it "renders the reaction as previously reacted if it was reacted on" do Reaction.create(user: user, reactable: comment, category: "like") get "/notifications" expect(response.body).to include "reaction-button reacted" end it "does not render the reaction as reacted if it was not reacted on" do expect(response.body).not_to include "reaction-button reacted" end end context "when a user has a new second level comment notification" do let(:user2) { create(:user) } let(:article) { create(:article, :with_notification_subscription, user_id: user.id) } let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article") } let(:second_comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article", parent_id: comment.id) } let(:third_comment) { create(:comment, user_id: user2.id, commentable_id: article.id, commentable_type: "Article", parent_id: second_comment.id) } before do sign_in user Notification.send_new_comment_notifications_without_delay(comment) Notification.send_new_comment_notifications_without_delay(second_comment) Notification.send_new_comment_notifications_without_delay(third_comment) get "/notifications" end it "contextualize comment notification text properly" do expect(response.body).to include "replied to a thread in" end it "contextualize comment title properly" do expect(response.body).to include CGI.escapeHTML("re: #{comment.title}") 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.add_role :trusted sign_in user perform_enqueued_jobs do Notification.send_moderation_notification(comment) end get "/notifications" end it "renders the proper message" do expect(response.body).to include "Since they are new to the community, could you leave a nice reply" end it "renders the article's path" do expect(response.body).to include article.path end it "renders the comment's processed HTML" do expect(response.body).to include comment.processed_html end end context "when a user should not receive 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 sign_in user perform_enqueued_jobs do Notification.send_moderation_notification(comment) end get "/notifications" end it "renders the proper message" do expect(response.body).not_to include "Since they are new to the community, could you leave a nice reply" end it "renders the article's path" do expect(response.body).not_to include article.path end it "renders the comment's processed HTML" do expect(response.body).not_to include comment.processed_html end end context "when a user has unsubscribed from mod roundrobin notifications" 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.add_role :trusted user.update(mod_roundrobin_notifications: false) sign_in user perform_enqueued_jobs do Notification.send_moderation_notification(comment) end get "/notifications" end it "renders the proper message" do expect(response.body).not_to include "Since they are new to the community, could you leave a nice reply" end it "renders the article's path" do expect(response.body).not_to include article.path end it "renders the comment's processed HTML" do expect(response.body).not_to include comment.processed_html end end context "when a user has a new welcome notification" do before do sign_in user end it "renders the welcome notification" do broadcast = create(:broadcast, :onboarding) sidekiq_perform_enqueued_jobs do Notification.send_welcome_notification(user.id) end 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) sidekiq_perform_enqueued_jobs do Notification.send_new_badge_achievement_notification(badge_achievement) end get "/notifications" end it "renders the badge's title" do expect(response.body).to include Badge.first.title end it "renders the rewarding context message" do expect(response.body).to include user.badge_achievements.first.rewarding_context_message end it "renders the badge's description" do expect(response.body).to include CGI.escapeHTML(Badge.first.description) end it "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 let(:mention) { create(:mention, mentionable: comment, user: user) } before do sidekiq_perform_enqueued_jobs do Notification.send_mention_notification(mention) end sign_in user get "/notifications" end it "renders the proper message" do expect(response.body).to include "mentioned you in a comment" end it "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) sidekiq_perform_enqueued_jobs do Notification.send_to_followers(article, "Published") end sign_in user2 get "/notifications" end it "renders the proper message" do expect(response.body).to include "made a new post:" end it "renders the article's path" do expect(response.body).to include article.path end it "renders the author's name" do expect(response.body).to include CGI.escapeHTML(article.user.name) end it "renders the reaction as previously reacted if it was reacted on" do Reaction.create(user: user2, reactable: article, category: "like") get "/notifications" expect(response.body).to include "reaction-button reacted" end it "does not render the reaction as reacted if it was not reacted on" do expect(response.body).not_to include "reaction-button reacted" end it "renders the article's published at" do expect(response.body).to include time_ago_in_words(article.published_at) end end context "when a user is an admin" do let(:admin) { create(:user, :super_admin) } let(:user2) { create(:user) } let(:article) { create(:article, user_id: user.id) } before do user2.follow(user) sidekiq_perform_enqueued_jobs do Notification.send_to_followers(article, "Published") end sign_in admin end it "can view other people's notifications" do get "/notifications?username=#{user2.username}" expect(response.body).to include "made a new post:" end end context "when filter is unknown" do it "does not raise an error" do sign_in user expect { get "/notifications/feed" }.not_to raise_error end end end end