diff --git a/CHANGELOG.md b/CHANGELOG.md index d9f4f81f1f4..ffc2bd92238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Design improvements https://github.com/consul/consul/pull/2170 - Adds timestamps to polls https://github.com/consul/consul/pull/2180 (Run `rake polls:initialize_timestamps` to initialize attributes created_at and updated_at with the current time for all existing polls, or manually through console set correct values) +- Improved Community design https://github.com/consul/consul/pull/1904 ### Deprecated diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index a652708d0b8..b91c5a5d7e5 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -336,7 +336,8 @@ .draft-panels, .debate-questions, .communities-show, -.topic-show { +.topic-show, +.milestone-content { p { word-wrap: break-word; diff --git a/app/controllers/admin/budget_investment_milestones_controller.rb b/app/controllers/admin/budget_investment_milestones_controller.rb index 2cea5f47190..73989939bfc 100644 --- a/app/controllers/admin/budget_investment_milestones_controller.rb +++ b/app/controllers/admin/budget_investment_milestones_controller.rb @@ -14,7 +14,8 @@ def create @milestone = Budget::Investment::Milestone.new(milestone_params) @milestone.investment = @investment if @milestone.save - redirect_to admin_budget_budget_investment_path(@investment.budget, @investment), notice: t('admin.milestones.create.notice') + redirect_to admin_budget_budget_investment_path(@investment.budget, @investment), + notice: t('admin.milestones.create.notice') else render :new end @@ -25,7 +26,8 @@ def edit def update if @milestone.update(milestone_params) - redirect_to admin_budget_budget_investment_path(@investment.budget, @investment), notice: t('admin.milestones.update.notice') + redirect_to admin_budget_budget_investment_path(@investment.budget, @investment), + notice: t('admin.milestones.update.notice') else render :edit end @@ -33,24 +35,25 @@ def update def destroy @milestone.destroy - redirect_to admin_budget_budget_investment_path(@investment.budget, @investment), notice: t('admin.milestones.delete.notice') + redirect_to admin_budget_budget_investment_path(@investment.budget, @investment), + notice: t('admin.milestones.delete.notice') end private def milestone_params params.require(:budget_investment_milestone) - .permit(:title, :description, :budget_investment_id, - image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) + .permit(:title, :description, :publication_date, :budget_investment_id, + image_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy], + documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) end def load_budget_investment - @investment = Budget::Investment.find params[:budget_investment_id] + @investment = Budget::Investment.find(params[:budget_investment_id]) end def load_budget_investment_milestone - @milestone = Budget::Investment::Milestone.find params[:id] + @milestone = Budget::Investment::Milestone.find(params[:id]) end - end diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 842c6cae352..1ebf674a3df 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -18,6 +18,7 @@ class Investment < ActiveRecord::Base 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 diff --git a/app/models/budget/investment/milestone.rb b/app/models/budget/investment/milestone.rb index b31b8640e90..13724be21f3 100644 --- a/app/models/budget/investment/milestone.rb +++ b/app/models/budget/investment/milestone.rb @@ -2,11 +2,16 @@ class Budget class Investment class Milestone < ActiveRecord::Base include Imageable + include Documentable + documentable max_documents_allowed: 3, + max_file_size: 3.megabytes, + accepted_content_types: [ "application/pdf" ] belongs_to :investment validates :title, presence: true validates :investment, presence: true + validates :publication_date, presence: true def self.title_max_length 80 diff --git a/app/models/comment.rb b/app/models/comment.rb index 8d92852a06e..e48a2407af0 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -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::Proposal Legislation::Annotation Topic SpendingProposal ProbeOption Poll).freeze + COMMENTABLE_TYPES = %w(Debate Proposal Budget::Investment Poll Poll::Question Topic Legislation::Question Legislation::Annotation Legislation::Proposal SpendingProposal ProbeOption).freeze acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases diff --git a/app/models/concerns/notifiable.rb b/app/models/concerns/notifiable.rb new file mode 100644 index 00000000000..f9748c64f12 --- /dev/null +++ b/app/models/concerns/notifiable.rb @@ -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 diff --git a/app/models/debate.rb b/app/models/debate.rb index 5d8f66e9087..aec77dc87fb 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -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 diff --git a/app/models/legislation/proposal.rb b/app/models/legislation/proposal.rb index 497ee77aaa1..62f2749408f 100644 --- a/app/models/legislation/proposal.rb +++ b/app/models/legislation/proposal.rb @@ -15,6 +15,7 @@ class Legislation::Proposal < ActiveRecord::Base include Followable include Communitable include Documentable + include Notifiable documentable max_documents_allowed: 3, max_file_size: 3.megabytes, diff --git a/app/models/legislation/question.rb b/app/models/legislation/question.rb index 71ce100eae6..2ca5fb39af1 100644 --- a/app/models/legislation/question.rb +++ b/app/models/legislation/question.rb @@ -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' diff --git a/app/models/notification.rb b/app/models/notification.rb index 84fc72f1c3e..de0e7c64526 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -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 @@ def self.add(user_id, notifiable) 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 @@ def notifiable_action end end - def linkable_resource - notifiable.is_a?(ProposalNotification) ? notifiable.proposal : notifiable - end - -end +end \ No newline at end of file diff --git a/app/models/poll.rb b/app/models/poll.rb index 7d57d81daba..c94b436f080 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -5,6 +5,7 @@ class Poll < ActiveRecord::Base include Imageable acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases + include Notifiable RECOUNT_DURATION = 1.week diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 8a1971cb26c..0b9c5c71ed4 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -13,6 +13,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, diff --git a/app/models/proposal_notification.rb b/app/models/proposal_notification.rb index 1bf6fccd252..0c184aa515c 100644 --- a/app/models/proposal_notification.rb +++ b/app/models/proposal_notification.rb @@ -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 @@ -35,4 +35,8 @@ def public_for_api? return true end + def notifiable + proposal + end + end diff --git a/app/models/topic.rb b/app/models/topic.rb index e0a73d58b12..11c921dd42f 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -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' diff --git a/app/views/admin/budget_investment_milestones/_form.html.erb b/app/views/admin/budget_investment_milestones/_form.html.erb index 8ed5b8cca03..93cd31f63fe 100644 --- a/app/views/admin/budget_investment_milestones/_form.html.erb +++ b/app/views/admin/budget_investment_milestones/_form.html.erb @@ -2,7 +2,16 @@ <%= f.text_field :title, maxlength: Budget::Investment::Milestone.title_max_length %> <%= f.text_area :description, rows: 5 %> + <%= f.text_field :publication_date, + value: @milestone.publication_date.present? ? l(@milestone.publication_date.to_date) : nil, + class: "js-calendar-full" %> + <%= render 'images/admin_image', imageable: @milestone, f: f %> +
+
+ <%= render 'documents/nested_documents', documentable: @milestone, f: f %> +
+ <%= f.submit nil, class: "button success" %> <% end %> diff --git a/app/views/admin/budget_investments/_milestones.html.erb b/app/views/admin/budget_investments/_milestones.html.erb index 4002d946877..6f30f8ad2d6 100644 --- a/app/views/admin/budget_investments/_milestones.html.erb +++ b/app/views/admin/budget_investments/_milestones.html.erb @@ -5,36 +5,53 @@ <%= t("admin.milestones.index.table_id") %> <%= t("admin.milestones.index.table_title") %> <%= t("admin.milestones.index.table_description") %> + <%= t("admin.milestones.index.table_publication_date") %> <%= t("admin.milestones.index.image") %> + <%= t("admin.milestones.index.documents") %> <%= t("admin.milestones.index.table_actions") %> <% @investment.milestones.each do |milestone| %> + <%= milestone.id %> - <%= milestone.id %> - - - <%= link_to milestone.title, edit_admin_budget_budget_investment_budget_investment_milestone_path(@investment.budget, @investment, milestone) %> + <%= link_to milestone.title, + edit_admin_budget_budget_investment_budget_investment_milestone_path(@investment.budget, + @investment, + milestone) %> + <%= milestone.description %> + <% if milestone.publication_date.present? %> + <%= l(milestone.publication_date.to_date) %> + <% end %> - <%= milestone.description %> + <%= link_to t("admin.milestones.index.show_image"), + milestone.image_url(:large), + target: :_blank if milestone.image.present? %> - <%= link_to t("admin.milestones.index.show_image"), milestone.image_url(:large), target: :_blank if milestone.image.present? %> + <% if milestone.documents.present? %> + <% milestone.documents.each do |document| %> + <%= link_to document.title, + document.attachment.url, + target: "_blank", + rel: "nofollow" %>
+ <% end %> + <% end %> - <%= link_to t("admin.milestones.index.delete"), admin_budget_budget_investment_budget_investment_milestone_path(@investment.budget, @investment, milestone), + <%= link_to t("admin.milestones.index.delete"), + admin_budget_budget_investment_budget_investment_milestone_path(@investment.budget, + @investment, + milestone), method: :delete, - class: 'button hollow alert expanded' %> + class: "button hollow alert expanded" %> <% end %> <% else %> -

- <%= t('admin.milestones.index.no_milestones') %> -

+

<%= t("admin.milestones.index.no_milestones") %>

<% end %> diff --git a/app/views/budgets/investments/_milestones.html.erb b/app/views/budgets/investments/_milestones.html.erb index 0d0c3898485..8065b614238 100644 --- a/app/views/budgets/investments/_milestones.html.erb +++ b/app/views/budgets/investments/_milestones.html.erb @@ -12,11 +12,28 @@
  • <%= milestone.title %>

    - - <%= t("budgets.investments.show.milestone_publish_date", publish_date: l(milestone.created_at.to_date)) %> - - <%= image_tag(milestone.image_url(:large), {alt: milestone.image.title, class: "margin", id: "image_#{milestone.id}"}) if milestone.image.present? %> + <% if milestone.publication_date.present? %> + + <%= t("budgets.investments.show.milestone_publication_date", + publication_date: l(milestone.publication_date.to_date)) %> + + <% end %> + <%= image_tag(milestone.image_url(:large), { alt: milestone.image.title, class: "margin", id: "image_#{milestone.id}" }) if milestone.image.present? %>

    <%= milestone.description %>

    + <% if milestone.documents.present? %> + + <% end %>
  • <% end %> diff --git a/app/views/comments/_form.html.erb b/app/views/comments/_form.html.erb index c9e4b7614b9..825b22788f3 100644 --- a/app/views/comments/_form.html.erb +++ b/app/views/comments/_form.html.erb @@ -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 %>
    diff --git a/app/views/devise/menu/_login_items.html.erb b/app/views/devise/menu/_login_items.html.erb index 3ca1b059ecb..ba24a92fb42 100644 --- a/app/views/devise/menu/_login_items.html.erb +++ b/app/views/devise/menu/_login_items.html.erb @@ -1,5 +1,5 @@ <% if user_signed_in? %> -
  • +
  • <%= link_to notifications_path, rel: "nofollow", class: "notifications" do %> <%= t("layouts.header.notifications") %> <% if current_user.notifications_count > 0 %> diff --git a/app/views/notifications/_notification.html.erb b/app/views/notifications/_notification.html.erb index 29bf008237d..a5c69550c86 100644 --- a/app/views/notifications/_notification.html.erb +++ b/app/views/notifications/_notification.html.erb @@ -1,5 +1,5 @@
  • - <% if notification.notifiable.present? %> + <% if notification.notifiable_available? %> <%= link_to notification do %>

    diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index cd7499c88ce..9b726f799dd 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -178,6 +178,7 @@ en: budget/investment/milestone: title: "Title" description: "Description" + publication_date: "Publication date" comment: body: "Comment" user: "User" diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index a07f5ec9656..8e13055bd6c 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -200,11 +200,13 @@ en: table_id: "ID" table_title: "Title" table_description: "Description" + table_publication_date: "Publication date" table_actions: "Actions" delete: "Delete milestone" no_milestones: "Don't have defined milestones" image: "Image" show_image: "Show image" + documents: "Documents" new: creating: Create milestone edit: @@ -1180,4 +1182,4 @@ en: updated_at: Updated at status_draft: Draft status_published: Published - locale: Language \ No newline at end of file + locale: Language diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 2a4f7aa9c97..fbef2107af7 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -112,7 +112,7 @@ en: comments_tab: Comments milestones_tab: Milestones no_milestones: Don't have defined milestones - milestone_publish_date: "Published %{publish_date}" + milestone_publication_date: "Published %{publication_date}" wrong_price_format: Only integer numbers investment: add: Vote diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index f7365cd848b..29ffa62e853 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -173,6 +173,7 @@ es: budget/investment/milestone: title: "Título" description: "Descripción" + publication_date: "Fecha de publicación" comment: body: "Comentario" user: "Usuario" @@ -341,4 +342,4 @@ es: record_invalid: "La validación falló: %{errors}" restrict_dependent_destroy: has_one: No se puede eliminar el registro porque existe un %{record} dependiente - has_many: No se puede eliminar el registro porque existen %{record} dependientes \ No newline at end of file + has_many: No se puede eliminar el registro porque existen %{record} dependientes diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 62f78bdef29..35af1726976 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -201,11 +201,13 @@ es: table_id: "ID" table_title: "Título" table_description: "Descripción" + table_publication_date: "Fecha de publicación" table_actions: "Acciones" delete: "Eliminar hito" no_milestones: "No hay hitos definidos" image: "Imagen" show_image: "Ver imagen" + documents: "Documentos" new: creating: Crear hito edit: diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index cc7a81c3646..1550de0e234 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -111,7 +111,7 @@ es: comments_tab: Comentarios milestones_tab: Seguimiento no_milestones: No hay hitos definidos - milestone_publish_date: "Publicado el %{publish_date}" + milestone_publication_date: "Publicado el %{publication_date}" wrong_price_format: Solo puede incluir caracteres numéricos investment: add: Votar diff --git a/db/migrate/20171212154048_add_publication_date_to_milestones.rb b/db/migrate/20171212154048_add_publication_date_to_milestones.rb new file mode 100644 index 00000000000..e5e37bb24cb --- /dev/null +++ b/db/migrate/20171212154048_add_publication_date_to_milestones.rb @@ -0,0 +1,13 @@ +class AddPublicationDateToMilestones < ActiveRecord::Migration + def up + change_table :budget_investment_milestones do |t| + t.datetime :publication_date + end + end + + def down + change_table :budget_investment_milestones do |t| + t.remove :publication_date + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 7e1d61a408a..720b992bbd7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -150,10 +150,11 @@ create_table "budget_investment_milestones", force: :cascade do |t| t.integer "investment_id" - t.string "title", limit: 80 + t.string "title", limit: 80 t.text "description" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.datetime "publication_date" end create_table "budget_investments", force: :cascade do |t| diff --git a/spec/factories.rb b/spec/factories.rb index 5cd425e0cba..be89f7bdea8 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -399,6 +399,7 @@ association :investment, factory: :budget_investment sequence(:title) { |n| "Budget investment milestone #{n} title" } description 'Milestone description' + publication_date Time.zone.today end factory :vote do diff --git a/spec/features/admin/budget_investment_milestones_spec.rb b/spec/features/admin/budget_investment_milestones_spec.rb index 7f9e3ff630a..0b198f6abaf 100644 --- a/spec/features/admin/budget_investment_milestones_spec.rb +++ b/spec/features/admin/budget_investment_milestones_spec.rb @@ -12,12 +12,17 @@ context "Index" do scenario 'Displaying milestones' do milestone = create(:budget_investment_milestone, investment: @investment) + create(:image, imageable: milestone) + document = create(:document, documentable: milestone) visit admin_budget_budget_investment_path(@investment.budget, @investment) expect(page).to have_content("Milestone") expect(page).to have_content(milestone.title) expect(page).to have_content(milestone.id) + expect(page).to have_content(milestone.publication_date.to_date) + expect(page).to have_link 'Show image' + expect(page).to have_link document.title end scenario 'Displaying no_milestones text' do @@ -36,11 +41,13 @@ fill_in 'budget_investment_milestone_title', with: 'New title milestone' fill_in 'budget_investment_milestone_description', with: 'New description milestone' + fill_in 'budget_investment_milestone_publication_date', with: Time.zone.today.to_date click_button 'Create milestone' expect(page).to have_content 'New title milestone' expect(page).to have_content 'New description milestone' + expect(page).to have_content Time.zone.today.to_date end scenario "Show validation errors on milestone form" do @@ -53,18 +60,20 @@ click_button 'Create milestone' within "#new_budget_investment_milestone" do - expect(page).to have_content "can't be blank" + expect(page).to have_content "can't be blank", count: 2 expect(page).to have_content 'New description milestone' end end end context "Edit" do - scenario "Change title and description" do + scenario "Change title, description and document names" do milestone = create(:budget_investment_milestone, investment: @investment) create(:image, imageable: milestone) + document = create(:document, documentable: milestone) visit admin_budget_budget_investment_path(@investment.budget, @investment) + expect(page).to have_link document.title click_link milestone.title @@ -72,12 +81,16 @@ fill_in 'budget_investment_milestone_title', with: 'Changed title' fill_in 'budget_investment_milestone_description', with: 'Changed description' + fill_in 'budget_investment_milestone_publication_date', with: Time.zone.today.to_date + fill_in 'budget_investment_milestone_documents_attributes_0_title', with: 'New document title' click_button 'Update milestone' expect(page).to have_content 'Changed title' expect(page).to have_content 'Changed description' + expect(page).to have_content Time.zone.today.to_date expect(page).to have_link 'Show image' + expect(page).to have_link 'New document title' end end diff --git a/spec/features/budgets/investments_spec.rb b/spec/features/budgets/investments_spec.rb index 45fc06b7b9c..55691f28c07 100644 --- a/spec/features/budgets/investments_spec.rb +++ b/spec/features/budgets/investments_spec.rb @@ -3,6 +3,10 @@ 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!") } @@ -517,9 +521,9 @@ def investments_order scenario "Show milestones", :js do user = create(:user) investment = create(:budget_investment) - milestone = create(:budget_investment_milestone, investment: investment, title: "New text to show", - created_at: DateTime.new(2015, 9, 19).utc) + milestone = create(:budget_investment_milestone, investment: investment, title: "New text to show") image = create(:image, imageable: milestone) + document = create(:document, documentable: milestone) login_as(user) visit budget_investment_path(budget_id: investment.budget.id, id: investment.id) @@ -529,8 +533,9 @@ def investments_order within("#tab-milestones") do expect(page).to have_content(milestone.title) expect(page).to have_content(milestone.description) - expect(page).to have_content("Published 2015-09-19") + expect(page).to have_content(Time.zone.today.to_date) expect(page.find("#image_#{milestone.id}")['alt']).to have_content image.title + expect(page).to have_link document.title end end diff --git a/spec/features/comments/legislation_questions_spec.rb b/spec/features/comments/legislation_questions_spec.rb index d21cefc4ee1..0c9c868ed28 100644 --- a/spec/features/comments/legislation_questions_spec.rb +++ b/spec/features/comments/legislation_questions_spec.rb @@ -2,6 +2,11 @@ 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 } diff --git a/spec/features/debates_spec.rb b/spec/features/debates_spec.rb index f30ea7f7b18..4c1d4a302a3 100644 --- a/spec/features/debates_spec.rb +++ b/spec/features/debates_spec.rb @@ -9,6 +9,10 @@ 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)] @@ -1111,4 +1115,5 @@ expect(page).to_not have_content("Featured") end end + end diff --git a/spec/features/legislation/proposals_spec.rb b/spec/features/legislation/proposals_spec.rb index e9eb11900eb..cbe4b623751 100644 --- a/spec/features/legislation/proposals_spec.rb +++ b/spec/features/legislation/proposals_spec.rb @@ -2,6 +2,11 @@ require 'sessions_helper' feature 'Legislation Proposals' do + + context "Concerns" do + it_behaves_like 'notifiable in-app', Legislation::Proposal + end + let(:user) { create(:user) } let(:user2) { create(:user) } let(:process) { create(:legislation_process) } @@ -55,4 +60,5 @@ def legislation_proposals_order all("[id^='legislation_proposal_']").collect { |e| e[:id] } end + end diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb index 0cb8392aa3c..42f3399c562 100644 --- a/spec/features/notifications_spec.rb +++ b/spec/features/notifications_spec.rb @@ -1,308 +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.reload - 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.reload - 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.reload - 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.reload - 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.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 - 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 - 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 - 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 + let(:user) { create :user } context "mark as read" do scenario "mark a single notification as read" do - user = create :user notification = create :notification, user: user login_as user @@ -317,7 +21,6 @@ end scenario "mark all notifications as read" do - user = create :user 2.times { create :notification, user: user } login_as user @@ -332,19 +35,6 @@ 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 diff --git a/spec/features/polls/polls_spec.rb b/spec/features/polls/polls_spec.rb index 3187fdc3d3a..c01fd28ab63 100644 --- a/spec/features/polls/polls_spec.rb +++ b/spec/features/polls/polls_spec.rb @@ -2,6 +2,10 @@ feature 'Polls' do + context "Concerns" do + it_behaves_like 'notifiable in-app', Poll + end + context '#index' do scenario 'Polls can be listed' do diff --git a/spec/features/proposal_notifications_spec.rb b/spec/features/proposal_notifications_spec.rb index 1e93f7e1353..5d7ad284cfc 100644 --- a/spec/features/proposal_notifications_spec.rb +++ b/spec/features/proposal_notifications_spec.rb @@ -195,6 +195,170 @@ 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) diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index 7286f5b0c30..4ab1e81f3d7 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -9,6 +9,10 @@ Setting['feature.proposals'] = true end + context "Concerns" do + it_behaves_like 'notifiable in-app', Proposal + end + context 'Index' do before do diff --git a/spec/features/topics_specs.rb b/spec/features/topics_specs.rb index a4c8c8ff9a4..6932a6604a0 100644 --- a/spec/features/topics_specs.rb +++ b/spec/features/topics_specs.rb @@ -2,6 +2,10 @@ feature 'Topics' do + context "Concerns" do + it_behaves_like 'notifiable in-app', Topic + end + context 'New' do scenario 'Create new topic link should redirect to sign up for anonymous users', :js do diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb index 786f5d48861..18347581b16 100644 --- a/spec/models/budget/investment_spec.rb +++ b/spec/models/budget/investment_spec.rb @@ -3,6 +3,10 @@ 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 diff --git a/spec/models/debate_spec.rb b/spec/models/debate_spec.rb index b48965c2c1f..2090a7a9ffd 100644 --- a/spec/models/debate_spec.rb +++ b/spec/models/debate_spec.rb @@ -4,7 +4,10 @@ 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 @@ -800,4 +803,5 @@ end end + end diff --git a/spec/models/legislation/process_spec.rb b/spec/models/legislation/process_spec.rb index 33460832b42..5783df2fec2 100644 --- a/spec/models/legislation/process_spec.rb +++ b/spec/models/legislation/process_spec.rb @@ -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 @@ expect(process.status).to eq(:open) end end + end diff --git a/spec/models/legislation/question_spec.rb b/spec/models/legislation/question_spec.rb index 216b9cbfd04..81d789445cd 100644 --- a/spec/models/legislation/question_spec.rb +++ b/spec/models/legislation/question_spec.rb @@ -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 @@ expect(question2.first_question_id).to eq(question1.id) end end + + describe "notifications" do + it_behaves_like 'notifiable' + end + end diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb index 12d31542189..0ffa39ae6bb 100644 --- a/spec/models/notification_spec.rb +++ b/spec/models/notification_spec.rb @@ -48,52 +48,21 @@ 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 diff --git a/spec/models/poll/poll_spec.rb b/spec/models/poll/poll_spec.rb index b31099bb134..0fe156a6251 100644 --- a/spec/models/poll/poll_spec.rb +++ b/spec/models/poll/poll_spec.rb @@ -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 @@ -286,4 +290,5 @@ end end + end diff --git a/spec/models/proposal_notification_spec.rb b/spec/models/proposal_notification_spec.rb index 88a98ebeef1..704555bdc83 100644 --- a/spec/models/proposal_notification_spec.rb +++ b/spec/models/proposal_notification_spec.rb @@ -78,4 +78,79 @@ 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 diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb index 5556b58f6ca..ad59cd1b816 100644 --- a/spec/models/proposal_spec.rb +++ b/spec/models/proposal_spec.rb @@ -4,7 +4,10 @@ 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 @@ -953,4 +956,5 @@ end end + end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 5071439fa02..bbe21d92081 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -3,6 +3,10 @@ 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 @@ end + describe "notifications" do + it_behaves_like 'notifiable' + end end diff --git a/spec/shared/features/notifiable_in_app.rb b/spec/shared/features/notifiable_in_app.rb new file mode 100644 index 00000000000..64e1e969119 --- /dev/null +++ b/spec/shared/features/notifiable_in_app.rb @@ -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 \ No newline at end of file diff --git a/spec/shared/models/notifiable.rb b/spec/shared/models/notifiable.rb new file mode 100644 index 00000000000..4c09a01cee1 --- /dev/null +++ b/spec/shared/models/notifiable.rb @@ -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 \ No newline at end of file diff --git a/spec/support/common_actions.rb b/spec/support/common_actions.rb index 5bebd6c3e1a..74d28d8192f 100644 --- a/spec/support/common_actions.rb +++ b/spec/support/common_actions.rb @@ -506,4 +506,32 @@ def vote_for_poll_via_booth def remove_token_from_vote_link page.execute_script("$('.js-question-answer')[0]['href'] = $('.js-question-answer')[0]['href'].match(/.+?(?=token)/)[0] + 'token='") 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