refactors notifications into concerns and shared examples

This commit is contained in:
rgarcia
2017-12-14 16:15:30 +01:00
parent 56a7c46c12
commit 2b52d26d82
35 changed files with 630 additions and 376 deletions

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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,

View File

@@ -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'

View File

@@ -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

View File

@@ -2,6 +2,7 @@ class Poll < ActiveRecord::Base
include Imageable
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
include Notifiable
RECOUNT_DURATION = 1.week

View File

@@ -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,

View File

@@ -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

View File

@@ -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'

View File

@@ -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">

View File

@@ -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 %>

View File

@@ -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>

View File

@@ -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!") }

View File

@@ -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 }

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View 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

View File

@@ -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