refactors notifications into concerns and shared examples
This commit is contained in:
@@ -18,6 +18,7 @@ class Budget
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
include Relationable
|
||||
include Notifiable
|
||||
|
||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :heading
|
||||
|
||||
@@ -2,8 +2,9 @@ class Comment < ActiveRecord::Base
|
||||
include Flaggable
|
||||
include HasPublicAuthor
|
||||
include Graphqlable
|
||||
include Notifiable
|
||||
|
||||
COMMENTABLE_TYPES = %w(Debate Proposal Budget::Investment Poll::Question Legislation::Question Legislation::Annotation Topic Legislation::Proposal Poll).freeze
|
||||
COMMENTABLE_TYPES = %w(Debate Proposal Budget::Investment Poll Topic Legislation::Question Legislation::Annotation Legislation::Proposal).freeze
|
||||
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
|
||||
35
app/models/concerns/notifiable.rb
Normal file
35
app/models/concerns/notifiable.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
module Notifiable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def notifiable_title
|
||||
case self.class.name
|
||||
when "ProposalNotification"
|
||||
proposal.title
|
||||
when "Comment"
|
||||
commentable.title
|
||||
else
|
||||
title
|
||||
end
|
||||
end
|
||||
|
||||
def notifiable_available?
|
||||
case self.class.name
|
||||
when "ProposalNotification"
|
||||
check_availability(proposal)
|
||||
when "Comment"
|
||||
check_availability(commentable)
|
||||
else
|
||||
check_availability(self)
|
||||
end
|
||||
end
|
||||
|
||||
def check_availability(resource)
|
||||
resource.present? &&
|
||||
resource.try(:hidden_at) == nil &&
|
||||
resource.try(:retired_at) == nil
|
||||
end
|
||||
|
||||
def linkable_resource
|
||||
is_a?(ProposalNotification) ? proposal : self
|
||||
end
|
||||
end
|
||||
@@ -10,6 +10,7 @@ class Debate < ActiveRecord::Base
|
||||
include HasPublicAuthor
|
||||
include Graphqlable
|
||||
include Relationable
|
||||
include Notifiable
|
||||
|
||||
acts_as_votable
|
||||
acts_as_paranoid column: :hidden_at
|
||||
|
||||
@@ -10,6 +10,7 @@ class Legislation::Proposal < ActiveRecord::Base
|
||||
include Followable
|
||||
include Communitable
|
||||
include Documentable
|
||||
include Notifiable
|
||||
|
||||
documentable max_documents_allowed: 3,
|
||||
max_file_size: 3.megabytes,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class Legislation::Question < ActiveRecord::Base
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
include Notifiable
|
||||
|
||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :process, class_name: 'Legislation::Process', foreign_key: 'legislation_process_id'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Notification < ActiveRecord::Base
|
||||
|
||||
belongs_to :user, counter_cache: true
|
||||
belongs_to :notifiable, polymorphic: true
|
||||
|
||||
@@ -7,6 +8,9 @@ class Notification < ActiveRecord::Base
|
||||
scope :not_emailed, -> { where(emailed_at: nil) }
|
||||
scope :for_render, -> { includes(:notifiable) }
|
||||
|
||||
delegate :notifiable_title, :notifiable_available?, :check_availability, :linkable_resource,
|
||||
to: :notifiable, allow_nil: true
|
||||
|
||||
def timestamp
|
||||
notifiable.created_at
|
||||
end
|
||||
@@ -25,17 +29,6 @@ class Notification < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def notifiable_title
|
||||
case notifiable.class.name
|
||||
when "ProposalNotification"
|
||||
notifiable.proposal.title
|
||||
when "Comment"
|
||||
notifiable.commentable.title
|
||||
else
|
||||
notifiable.title
|
||||
end
|
||||
end
|
||||
|
||||
def notifiable_action
|
||||
case notifiable_type
|
||||
when "ProposalNotification"
|
||||
@@ -47,8 +40,4 @@ class Notification < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def linkable_resource
|
||||
notifiable.is_a?(ProposalNotification) ? notifiable.proposal : notifiable
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -2,6 +2,7 @@ class Poll < ActiveRecord::Base
|
||||
include Imageable
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
include Notifiable
|
||||
|
||||
RECOUNT_DURATION = 1.week
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ class Proposal < ActiveRecord::Base
|
||||
include Communitable
|
||||
include Imageable
|
||||
include Mappable
|
||||
include Notifiable
|
||||
include Documentable
|
||||
documentable max_documents_allowed: 3,
|
||||
max_file_size: 3.megabytes,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class ProposalNotification < ActiveRecord::Base
|
||||
|
||||
include Graphqlable
|
||||
include Notifiable
|
||||
|
||||
belongs_to :author, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :proposal
|
||||
@@ -21,4 +21,8 @@ class ProposalNotification < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def notifiable
|
||||
proposal
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class Topic < ActiveRecord::Base
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
include Notifiable
|
||||
|
||||
belongs_to :community
|
||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<%= f.hidden_field :commentable_id, value: commentable.id %>
|
||||
<%= f.hidden_field :parent_id, value: parent_id %>
|
||||
|
||||
<%= f.submit comment_button_text(parent_id, commentable), class: "button" %>
|
||||
<%= f.submit comment_button_text(parent_id, commentable), class: "button", id: "publish_comment" %>
|
||||
|
||||
<% if can? :comment_as_moderator, commentable %>
|
||||
<div class="float-right">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<% if user_signed_in? %>
|
||||
<li>
|
||||
<li id="notifications">
|
||||
<%= link_to notifications_path, rel: "nofollow", class: "notifications" do %>
|
||||
<span class="show-for-sr"><%= t("layouts.header.notifications") %></span>
|
||||
<% if current_user.notifications_count > 0 %>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<li id="<%= dom_id(notification) %>" class="notification">
|
||||
<% if notification.notifiable.present? %>
|
||||
<% if notification.notifiable_available? %>
|
||||
<%= link_to notification do %>
|
||||
<p>
|
||||
<em>
|
||||
|
||||
@@ -3,6 +3,10 @@ require 'sessions_helper'
|
||||
|
||||
feature 'Budget Investments' do
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Budget::Investment
|
||||
end
|
||||
|
||||
let(:author) { create(:user, :level_two, username: 'Isabel') }
|
||||
let(:budget) { create(:budget, name: "Big Budget") }
|
||||
let(:other_budget) { create(:budget, name: "What a Budget!") }
|
||||
|
||||
@@ -2,6 +2,11 @@ require 'rails_helper'
|
||||
include ActionView::Helpers::DateHelper
|
||||
|
||||
feature 'Commenting legislation questions' do
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Legislation::Question
|
||||
end
|
||||
|
||||
let(:user) { create :user, :level_two }
|
||||
let(:process) { create :legislation_process, :in_debate_phase }
|
||||
let(:legislation_question) { create :legislation_question, process: process }
|
||||
|
||||
@@ -9,6 +9,10 @@ feature 'Debates' do
|
||||
Setting['feature.debates'] = true
|
||||
end
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Debate
|
||||
end
|
||||
|
||||
scenario 'Index' do
|
||||
debates = [create(:debate), create(:debate), create(:debate)]
|
||||
|
||||
@@ -1047,4 +1051,5 @@ feature 'Debates' do
|
||||
expect(page).to_not have_content("Featured")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
9
spec/features/legislation/proposals_spec.rb
Normal file
9
spec/features/legislation/proposals_spec.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Legislation Proposals' do
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Legislation::Proposal
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,309 +1,12 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature "Notifications" do
|
||||
let(:admin_user) { create :user }
|
||||
let(:administrator) do
|
||||
create(:administrator, user: admin_user)
|
||||
admin_user
|
||||
end
|
||||
let(:author) { create :user }
|
||||
|
||||
let(:user) { create :user }
|
||||
let(:debate) { create :debate, author: author }
|
||||
let(:proposal) { create :proposal, author: author }
|
||||
let(:process) { create :legislation_process, :in_debate_phase }
|
||||
let(:legislation_question) { create(:legislation_question, process: process, author: administrator) }
|
||||
let(:legislation_annotation) { create(:legislation_annotation, author: author) }
|
||||
|
||||
let(:topic) do
|
||||
proposal = create(:proposal)
|
||||
community = proposal.community
|
||||
create(:topic, community: community, author: author)
|
||||
end
|
||||
|
||||
scenario "User commented on my debate", :js do
|
||||
create(:notification, notifiable: debate, user: author)
|
||||
login_as author
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
|
||||
expect(page).to have_content "Someone commented on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "User commented on my legislation question", :js do
|
||||
create(:notification, notifiable: legislation_question, user: administrator)
|
||||
login_as administrator
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
|
||||
expect(page).to have_content "Someone commented on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "User commented on my topic", :js do
|
||||
create(:notification, notifiable: topic, user: author)
|
||||
login_as author
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
|
||||
expect(page).to have_content "Someone commented on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "Multiple comments on my proposal", :js do
|
||||
login_as user
|
||||
visit proposal_path proposal
|
||||
|
||||
fill_in "comment-body-proposal_#{proposal.id}", with: "I agree"
|
||||
click_button "Publish comment"
|
||||
within "#comments" do
|
||||
expect(page).to have_content "I agree"
|
||||
end
|
||||
|
||||
logout
|
||||
login_as create(:user)
|
||||
visit proposal_path proposal
|
||||
|
||||
fill_in "comment-body-proposal_#{proposal.id}", with: "I disagree"
|
||||
click_button "Publish comment"
|
||||
within "#comments" do
|
||||
expect(page).to have_content "I disagree"
|
||||
end
|
||||
|
||||
logout
|
||||
login_as author
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
|
||||
expect(page).to have_content "There are 2 new comments on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "User replied to my comment", :js do
|
||||
comment = create :comment, commentable: debate, user: author
|
||||
login_as user
|
||||
visit debate_path debate
|
||||
|
||||
click_link "Reply"
|
||||
within "#js-comment-form-comment_#{comment.id}" do
|
||||
fill_in "comment-body-comment_#{comment.id}", with: "I replied to your comment"
|
||||
click_button "Publish reply"
|
||||
end
|
||||
|
||||
within "#comment_#{comment.id}" do
|
||||
expect(page).to have_content "I replied to your comment"
|
||||
end
|
||||
|
||||
logout
|
||||
|
||||
login_as author
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "Someone replied to your comment on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "Multiple replies to my comment", :js do
|
||||
comment = create :comment, commentable: debate, user: author
|
||||
3.times do |n|
|
||||
login_as create(:user)
|
||||
visit debate_path debate
|
||||
|
||||
within("#comment_#{comment.id}_reply") { click_link "Reply" }
|
||||
within "#js-comment-form-comment_#{comment.id}" do
|
||||
fill_in "comment-body-comment_#{comment.id}", with: "Reply number #{n}"
|
||||
click_button "Publish reply"
|
||||
end
|
||||
|
||||
within "#comment_#{comment.id}" do
|
||||
expect(page).to have_content "Reply number #{n}"
|
||||
end
|
||||
logout
|
||||
end
|
||||
|
||||
login_as author
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There are 3 new replies to your comment on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "Author commented on his own debate", :js do
|
||||
login_as author
|
||||
visit debate_path debate
|
||||
|
||||
fill_in "comment-body-debate_#{debate.id}", with: "I commented on my own debate"
|
||||
click_button "Publish comment"
|
||||
within "#comments" do
|
||||
expect(page).to have_content "I commented on my own debate"
|
||||
end
|
||||
|
||||
find(".icon-no-notification").click
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
scenario "Author replied to his own comment", :js do
|
||||
comment = create :comment, commentable: debate, user: author
|
||||
login_as author
|
||||
visit debate_path debate
|
||||
|
||||
click_link "Reply"
|
||||
within "#js-comment-form-comment_#{comment.id}" do
|
||||
fill_in "comment-body-comment_#{comment.id}", with: "I replied to my own comment"
|
||||
click_button "Publish reply"
|
||||
end
|
||||
|
||||
within "#comment_#{comment.id}" do
|
||||
expect(page).to have_content "I replied to my own comment"
|
||||
end
|
||||
|
||||
find(".icon-no-notification")
|
||||
|
||||
visit notifications_path
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
context "Proposal notification" do
|
||||
|
||||
scenario "Voters should receive a notification", :js do
|
||||
author = create(:user)
|
||||
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
user3 = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:vote, voter: user1, votable: proposal, vote_flag: true)
|
||||
create(:vote, voter: user2, votable: proposal, vote_flag: true)
|
||||
|
||||
login_as(author)
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
|
||||
logout
|
||||
login_as user1
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user1 = Notification.where(user: user1).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user1)}']"
|
||||
|
||||
logout
|
||||
login_as user2
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user2 = Notification.where(user: user2).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user2)}']"
|
||||
|
||||
logout
|
||||
login_as user3
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-no-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
scenario "Followers should receive a notification", :js do
|
||||
author = create(:user)
|
||||
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
user3 = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:follow, :followed_proposal, user: user1, followable: proposal)
|
||||
create(:follow, :followed_proposal, user: user2, followable: proposal)
|
||||
|
||||
login_as author.reload
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
|
||||
logout
|
||||
login_as user1.reload
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user1 = Notification.where(user: user1).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user1)}']"
|
||||
|
||||
logout
|
||||
login_as user2.reload
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user2 = Notification.where(user: user2).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user2)}']"
|
||||
|
||||
logout
|
||||
login_as user3.reload
|
||||
visit root_path
|
||||
|
||||
find(".icon-no-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
pending "group notifications for the same proposal"
|
||||
end
|
||||
|
||||
context "mark as read" do
|
||||
|
||||
scenario "mark a single notification as read" do
|
||||
user = create :user
|
||||
notification = create :notification, user: user
|
||||
|
||||
login_as user
|
||||
@@ -318,7 +21,6 @@ feature "Notifications" do
|
||||
end
|
||||
|
||||
scenario "mark all notifications as read" do
|
||||
user = create :user
|
||||
2.times { create :notification, user: user }
|
||||
|
||||
login_as user
|
||||
@@ -333,19 +35,6 @@ feature "Notifications" do
|
||||
|
||||
end
|
||||
|
||||
scenario "Notifiable hidden", :js do
|
||||
create(:notification, notifiable: debate, user: author)
|
||||
debate.hide
|
||||
|
||||
login_as author
|
||||
visit root_path
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "This resource is not available anymore"
|
||||
expect(page).to_not have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "no notifications" do
|
||||
login_as user
|
||||
visit notifications_path
|
||||
|
||||
@@ -2,6 +2,10 @@ require 'rails_helper'
|
||||
|
||||
feature 'Polls' do
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Poll
|
||||
end
|
||||
|
||||
context '#index' do
|
||||
|
||||
scenario 'Polls can be listed' do
|
||||
|
||||
@@ -195,6 +195,170 @@ feature 'Proposal Notifications' do
|
||||
|
||||
end
|
||||
|
||||
context "In-app notifications from the proposal's author" do
|
||||
|
||||
scenario "Voters should receive a notification", :js do
|
||||
author = create(:user)
|
||||
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
user3 = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:vote, voter: user1, votable: proposal, vote_flag: true)
|
||||
create(:vote, voter: user2, votable: proposal, vote_flag: true)
|
||||
|
||||
login_as(author)
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
|
||||
logout
|
||||
login_as user1
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user1 = Notification.where(user: user1).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user1)}']"
|
||||
|
||||
logout
|
||||
login_as user2
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user2 = Notification.where(user: user2).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user2)}']"
|
||||
|
||||
logout
|
||||
login_as user3
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-no-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
scenario "Followers should receive a notification", :js do
|
||||
author = create(:user)
|
||||
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
user3 = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:follow, :followed_proposal, user: user1, followable: proposal)
|
||||
create(:follow, :followed_proposal, user: user2, followable: proposal)
|
||||
|
||||
login_as author.reload
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
|
||||
logout
|
||||
login_as user1.reload
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user1 = Notification.where(user: user1).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user1)}']"
|
||||
|
||||
logout
|
||||
login_as user2.reload
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user2 = Notification.where(user: user2).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There is one new notification on #{proposal.title}"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification_for_user2)}']"
|
||||
|
||||
logout
|
||||
login_as user3.reload
|
||||
visit root_path
|
||||
|
||||
find(".icon-no-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
scenario "Proposal hidden", :js do
|
||||
author = create(:user)
|
||||
user = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:vote, voter: user, votable: proposal, vote_flag: true)
|
||||
|
||||
login_as(author)
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
|
||||
fill_in 'proposal_notification_title', with: "Thank you for supporting my proposal"
|
||||
fill_in 'proposal_notification_body', with: "Please share it with others so we can make it happen!"
|
||||
click_button "Send message"
|
||||
|
||||
expect(page).to have_content "Your message has been sent correctly."
|
||||
|
||||
proposal.hide
|
||||
|
||||
logout
|
||||
login_as user
|
||||
visit root_path
|
||||
visit root_path
|
||||
|
||||
find(".icon-notification").click
|
||||
|
||||
notification_for_user = Notification.where(user: user).first
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "This resource is not available anymore"
|
||||
expect(page).to_not have_xpath "//a[@href='#{notification_path(notification_for_user)}']"
|
||||
end
|
||||
|
||||
scenario "Proposal retired by author", :js do
|
||||
author = create(:user)
|
||||
user = create(:user)
|
||||
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
create(:vote, voter: user, votable: proposal, vote_flag: true)
|
||||
|
||||
login_as(author)
|
||||
visit root_path
|
||||
|
||||
visit new_proposal_notification_path(proposal_id: proposal.id)
|
||||
end
|
||||
|
||||
pending "group notifications for the same proposal"
|
||||
end
|
||||
|
||||
scenario "Error messages" do
|
||||
author = create(:user)
|
||||
proposal = create(:proposal, author: author)
|
||||
|
||||
@@ -9,6 +9,10 @@ feature 'Proposals' do
|
||||
Setting['feature.proposals'] = true
|
||||
end
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Proposal
|
||||
end
|
||||
|
||||
context 'Index' do
|
||||
scenario 'Lists featured and regular proposals' do
|
||||
featured_proposals = create_featured_proposals
|
||||
|
||||
@@ -2,6 +2,10 @@ require 'rails_helper'
|
||||
|
||||
feature 'Topics' do
|
||||
|
||||
context "Concerns" do
|
||||
it_behaves_like 'notifiable in-app', Topic
|
||||
end
|
||||
|
||||
context 'New' do
|
||||
|
||||
scenario 'Should display disabled button to new topic page without user logged', :js do
|
||||
|
||||
@@ -3,6 +3,10 @@ require 'rails_helper'
|
||||
describe Budget::Investment do
|
||||
let(:investment) { build(:budget_investment) }
|
||||
|
||||
describe "Concerns" do
|
||||
it_behaves_like "notifiable"
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
expect(investment).to be_valid
|
||||
end
|
||||
|
||||
@@ -4,7 +4,10 @@ require 'rails_helper'
|
||||
describe Debate do
|
||||
let(:debate) { build(:debate) }
|
||||
|
||||
it_behaves_like "has_public_author"
|
||||
describe "Concerns" do
|
||||
it_behaves_like "has_public_author"
|
||||
it_behaves_like "notifiable"
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
expect(debate).to be_valid
|
||||
@@ -763,4 +766,5 @@ describe Debate do
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Legislation::Process, type: :model do
|
||||
describe Legislation::Process do
|
||||
let(:process) { create(:legislation_process) }
|
||||
|
||||
it "should be valid" do
|
||||
@@ -115,4 +115,5 @@ RSpec.describe Legislation::Process, type: :model do
|
||||
expect(process.status).to eq(:open)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Legislation::Question, type: :model do
|
||||
describe Legislation::Question do
|
||||
let(:question) { create(:legislation_question) }
|
||||
|
||||
describe "Concerns" do
|
||||
it_behaves_like "notifiable"
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
expect(question).to be_valid
|
||||
end
|
||||
@@ -59,4 +63,9 @@ RSpec.describe Legislation::Question, type: :model do
|
||||
expect(question2.first_question_id).to eq(question1.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "notifications" do
|
||||
it_behaves_like 'notifiable'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -48,52 +48,21 @@ describe Notification do
|
||||
end
|
||||
|
||||
describe "#notification_action" do
|
||||
it "returns correct text when someone comments on your commentable" do
|
||||
debate = create(:debate)
|
||||
notification = create(:notification, notifiable: debate)
|
||||
let(:notifiable) { create(:proposal) }
|
||||
|
||||
it "returns correct action when someone comments on your commentable" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.notifiable_action).to eq "comments_on"
|
||||
end
|
||||
|
||||
it "returns correct text when someone replies to your comment" do
|
||||
debate = create(:debate)
|
||||
debate_comment = create(:comment, commentable: debate)
|
||||
notification = create(:notification, notifiable: debate_comment)
|
||||
it "returns correct action when someone replies to your comment" do
|
||||
comment = create(:comment, commentable: notifiable)
|
||||
notification = create(:notification, notifiable: comment)
|
||||
|
||||
expect(notification.notifiable_action).to eq "replies_to"
|
||||
end
|
||||
|
||||
it "returns correct text when the author created a proposal notification" do
|
||||
proposal_notification = create(:proposal_notification)
|
||||
notification = create(:notification, notifiable: proposal_notification)
|
||||
|
||||
expect(notification.notifiable_action).to eq "proposal_notification"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#notification_title" do
|
||||
it "returns the commentable title when it's a root comment" do
|
||||
debate = create(:debate, title: "Save the whales")
|
||||
notification = create(:notification, notifiable: debate)
|
||||
|
||||
expect(notification.notifiable_title).to eq "Save the whales"
|
||||
end
|
||||
|
||||
it "returns the commentable title when it's a reply to a root comment" do
|
||||
debate = create(:debate, title: "Save the whales")
|
||||
debate_comment = create(:comment, commentable: debate)
|
||||
notification = create(:notification, notifiable: debate_comment)
|
||||
|
||||
expect(notification.notifiable_title).to eq "Save the whales"
|
||||
end
|
||||
|
||||
it "returns the commentable title when it's an author's proposals notification" do
|
||||
proposal = create(:proposal, title: "Save the whales")
|
||||
proposal_notification = create(:proposal_notification, proposal: proposal)
|
||||
notification = create(:notification, notifiable: proposal_notification)
|
||||
|
||||
expect(notification.notifiable_title).to eq "Save the whales"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe :poll do
|
||||
describe Poll do
|
||||
|
||||
let(:poll) { build(:poll) }
|
||||
|
||||
describe "Concerns" do
|
||||
it_behaves_like "notifiable"
|
||||
end
|
||||
|
||||
describe "validations" do
|
||||
it "should be valid" do
|
||||
expect(poll).to be_valid
|
||||
@@ -199,4 +203,5 @@ describe :poll do
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -78,4 +78,79 @@ describe ProposalNotification do
|
||||
|
||||
end
|
||||
|
||||
describe "notifications in-app" do
|
||||
|
||||
let(:notifiable) { create(model_name(described_class)) }
|
||||
let(:proposal) { notifiable.proposal }
|
||||
|
||||
describe "#notification_title" do
|
||||
|
||||
it "returns the proposal title" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.notifiable_title).to eq notifiable.proposal.title
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#notification_action" do
|
||||
|
||||
it "returns the correct action" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.notifiable_action).to eq "proposal_notification"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "notifiable_available?" do
|
||||
|
||||
it "returns true when the proposal is available" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.notifiable_available?).to be(true)
|
||||
end
|
||||
|
||||
it "returns false when the proposal is not available" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
notifiable.proposal.destroy
|
||||
|
||||
expect(notification.notifiable_available?).to be(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "check_availability" do
|
||||
|
||||
it "returns true if the resource is present, not hidden, nor retired" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.check_availability(proposal)).to be(true)
|
||||
end
|
||||
|
||||
it "returns false if the resource is not present" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
notifiable.proposal.really_destroy!
|
||||
expect(notification.check_availability(proposal)).to be(false)
|
||||
end
|
||||
|
||||
it "returns false if the resource is hidden" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
notifiable.proposal.hide
|
||||
expect(notification.check_availability(proposal)).to be(false)
|
||||
end
|
||||
|
||||
it "returns false if the resource is retired" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
notifiable.proposal.update(retired_at: Time.now)
|
||||
expect(notification.check_availability(proposal)).to be(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,10 @@ require 'rails_helper'
|
||||
describe Proposal do
|
||||
let(:proposal) { build(:proposal) }
|
||||
|
||||
it_behaves_like "has_public_author"
|
||||
describe "Concerns" do
|
||||
it_behaves_like "has_public_author"
|
||||
it_behaves_like "notifiable"
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
expect(proposal).to be_valid
|
||||
@@ -950,4 +953,5 @@ describe Proposal do
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -3,6 +3,10 @@ require 'rails_helper'
|
||||
describe Topic do
|
||||
let(:topic) { build(:topic) }
|
||||
|
||||
describe "Concerns" do
|
||||
it_behaves_like "notifiable"
|
||||
end
|
||||
|
||||
it "should be valid" do
|
||||
expect(topic).to be_valid
|
||||
end
|
||||
@@ -71,4 +75,7 @@ describe Topic do
|
||||
|
||||
end
|
||||
|
||||
describe "notifications" do
|
||||
it_behaves_like 'notifiable'
|
||||
end
|
||||
end
|
||||
|
||||
137
spec/shared/features/notifiable_in_app.rb
Normal file
137
spec/shared/features/notifiable_in_app.rb
Normal file
@@ -0,0 +1,137 @@
|
||||
shared_examples "notifiable in-app" do |described_class|
|
||||
|
||||
let(:author) { create(:user, :verified) }
|
||||
let!(:notifiable) { create(model_name(described_class), author: author) }
|
||||
|
||||
scenario "A user commented on my notifiable", :js do
|
||||
notification = create(:notification, notifiable: notifiable, user: author)
|
||||
|
||||
login_as author
|
||||
visit root_path
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "Someone commented on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(notification)}']"
|
||||
end
|
||||
|
||||
scenario "Multiple users commented on my notifiable", :js do
|
||||
3.times do
|
||||
login_as(create(:user, :verified))
|
||||
|
||||
visit path_for(notifiable)
|
||||
|
||||
fill_in comment_body(notifiable), with: "I agree"
|
||||
click_button "publish_comment"
|
||||
within "#comments" do
|
||||
expect(page).to have_content "I agree"
|
||||
end
|
||||
end
|
||||
|
||||
logout
|
||||
login_as author
|
||||
visit root_path
|
||||
visit root_path
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There are 3 new comments on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "A user replied to my comment", :js do
|
||||
comment = create :comment, commentable: notifiable, user: author
|
||||
|
||||
login_as(create(:user, :verified))
|
||||
visit path_for(notifiable)
|
||||
|
||||
click_link "Reply"
|
||||
within "#js-comment-form-comment_#{comment.id}" do
|
||||
fill_in "comment-body-comment_#{comment.id}", with: "I replied to your comment"
|
||||
click_button "Publish reply"
|
||||
end
|
||||
|
||||
within "#comment_#{comment.id}" do
|
||||
expect(page).to have_content "I replied to your comment"
|
||||
end
|
||||
|
||||
logout
|
||||
login_as author
|
||||
visit root_path
|
||||
visit root_path
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "Someone replied to your comment on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "Multiple replies to my comment", :js do
|
||||
comment = create :comment, commentable: notifiable, user: author
|
||||
|
||||
3.times do |n|
|
||||
login_as(create(:user, :verified))
|
||||
visit path_for(notifiable)
|
||||
|
||||
within("#comment_#{comment.id}_reply") { click_link "Reply" }
|
||||
within "#js-comment-form-comment_#{comment.id}" do
|
||||
fill_in "comment-body-comment_#{comment.id}", with: "Reply number #{n}"
|
||||
click_button "Publish reply"
|
||||
end
|
||||
|
||||
within "#comment_#{comment.id}" do
|
||||
expect(page).to have_content "Reply number #{n}"
|
||||
end
|
||||
logout
|
||||
end
|
||||
|
||||
login_as author
|
||||
visit root_path
|
||||
visit root_path
|
||||
find(".icon-notification").click
|
||||
|
||||
expect(page).to have_css ".notification", count: 1
|
||||
expect(page).to have_content "There are 3 new replies to your comment on"
|
||||
expect(page).to have_xpath "//a[@href='#{notification_path(Notification.last)}']"
|
||||
end
|
||||
|
||||
scenario "Author commented on his own notifiable", :js do
|
||||
login_as(author)
|
||||
visit path_for(notifiable)
|
||||
|
||||
fill_in comment_body(notifiable), with: "I commented on my own notifiable"
|
||||
click_button "publish_comment"
|
||||
within "#comments" do
|
||||
expect(page).to have_content "I commented on my own notifiable"
|
||||
end
|
||||
|
||||
within("#notifications") do
|
||||
find(".icon-no-notification").click
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Author replied to his own comment", :js do
|
||||
comment = create :comment, commentable: notifiable, user: author
|
||||
|
||||
login_as author
|
||||
visit path_for(notifiable)
|
||||
|
||||
click_link "Reply"
|
||||
within "#js-comment-form-comment_#{comment.id}" do
|
||||
fill_in "comment-body-comment_#{comment.id}", with: "I replied to my own comment"
|
||||
click_button "Publish reply"
|
||||
end
|
||||
|
||||
within "#comment_#{comment.id}" do
|
||||
expect(page).to have_content "I replied to my own comment"
|
||||
end
|
||||
|
||||
within("#notifications") do
|
||||
find(".icon-no-notification").click
|
||||
expect(page).to have_css ".notification", count: 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
88
spec/shared/models/notifiable.rb
Normal file
88
spec/shared/models/notifiable.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
shared_examples "notifiable" do
|
||||
|
||||
let(:notifiable) { create(model_name(described_class)) }
|
||||
|
||||
describe "#notification_title" do
|
||||
|
||||
it "returns the notifiable title when it's a root comment" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.notifiable_title).to eq notifiable.title
|
||||
end
|
||||
|
||||
it "returns the notifiable title when it's a reply to a root comment" do
|
||||
comment = create(:comment, commentable: notifiable)
|
||||
notification = create(:notification, notifiable: comment)
|
||||
|
||||
expect(notification.notifiable_title).to eq notifiable.title
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "notifiable_available?" do
|
||||
|
||||
it "returns true when it's a root comment and the notifiable is available" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
expect(notification.notifiable_available?).to be(true)
|
||||
end
|
||||
|
||||
it "returns true when it's a reply to comment and the notifiable is available" do
|
||||
comment = create(:comment, commentable: notifiable)
|
||||
notification = create(:notification, notifiable: comment)
|
||||
|
||||
expect(notification.notifiable_available?).to be(true)
|
||||
end
|
||||
|
||||
it "returns false when it's a root comment and the notifiable has been hidden" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
notifiable.hide
|
||||
notification.reload
|
||||
|
||||
expect(notification.notifiable_available?).to_not be(true)
|
||||
end
|
||||
|
||||
it "returns false when it's a reply to comment and the commentable has been hidden" do
|
||||
comment = create(:comment, commentable: notifiable)
|
||||
notification = create(:notification, notifiable: comment)
|
||||
|
||||
notifiable.hide
|
||||
notification.reload
|
||||
|
||||
expect(notification.notifiable_available?).to be(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "check_availability" do
|
||||
|
||||
it "returns true if the resource is present, not hidden, nor retired" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
expect(notification.check_availability(notifiable)).to be(true)
|
||||
end
|
||||
|
||||
it "returns false if the resource is not present" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
notifiable.really_destroy!
|
||||
expect(notification.check_availability(notifiable)).to be(false)
|
||||
end
|
||||
|
||||
it "returns false if the resource is not hidden" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
notifiable.hide
|
||||
expect(notification.check_availability(notifiable)).to be(false)
|
||||
end
|
||||
|
||||
it "returns false if the resource is retired" do
|
||||
notification = create(:notification, notifiable: notifiable)
|
||||
|
||||
if notifiable.respond_to?(:retired_at)
|
||||
notifiable.update(retired_at: Time.now)
|
||||
expect(notification.check_availability(notifiable)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -330,4 +330,31 @@ module CommonActions
|
||||
expect(Poll::Voter.count).to eq(1)
|
||||
end
|
||||
|
||||
def model_name(described_class)
|
||||
return :proposal_notification if described_class == ProposalNotification
|
||||
|
||||
described_class.name.gsub("::", "_").downcase.to_sym
|
||||
end
|
||||
|
||||
def comment_body(resource)
|
||||
"comment-body-#{resource.class.name.gsub("::", "_").downcase.to_sym}_#{resource.id}"
|
||||
end
|
||||
|
||||
def path_for(resource)
|
||||
nested_path_for(resource) || url_for([resource, only_path: true])
|
||||
end
|
||||
|
||||
def nested_path_for(resource)
|
||||
case resource.class.name
|
||||
when "Legislation::Question"
|
||||
legislation_process_question_path(resource.process, resource)
|
||||
when "Legislation::Proposal"
|
||||
legislation_process_proposal_path(resource.process, resource)
|
||||
when "Budget::Investment"
|
||||
budget_investment_path(resource.budget, resource)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user