diff --git a/app/assets/fonts/icons.eot b/app/assets/fonts/icons.eot index 3046c50cc..ced98a9b4 100644 Binary files a/app/assets/fonts/icons.eot and b/app/assets/fonts/icons.eot differ diff --git a/app/assets/fonts/icons.svg b/app/assets/fonts/icons.svg index c107e740d..d92b9a44a 100644 --- a/app/assets/fonts/icons.svg +++ b/app/assets/fonts/icons.svg @@ -19,7 +19,6 @@ - @@ -30,12 +29,10 @@ - - @@ -45,4 +42,8 @@ + + + + diff --git a/app/assets/fonts/icons.ttf b/app/assets/fonts/icons.ttf index b933f1c3f..f3486cf96 100644 Binary files a/app/assets/fonts/icons.ttf and b/app/assets/fonts/icons.ttf differ diff --git a/app/assets/fonts/icons.woff b/app/assets/fonts/icons.woff index feb047bd0..9045aa563 100644 Binary files a/app/assets/fonts/icons.woff and b/app/assets/fonts/icons.woff differ diff --git a/app/assets/stylesheets/icons.scss b/app/assets/stylesheets/icons.scss index eda8b979d..46e24e4cf 100644 --- a/app/assets/stylesheets/icons.scss +++ b/app/assets/stylesheets/icons.scss @@ -38,116 +38,119 @@ } .icon-angle-down:before { - content: "a"; + content: "\61"; } .icon-angle-left:before { - content: "b"; + content: "\62"; } .icon-angle-right:before { - content: "c"; + content: "\63"; } .icon-angle-up:before { - content: "d"; + content: "\64"; } .icon-comments:before { - content: "e"; + content: "\65"; } .icon-twitter:before { - content: "f"; + content: "\66"; } .icon-calendar:before { - content: "g"; + content: "\67"; } .icon-debates:before { - content: "i"; + content: "\69"; } .icon-unlike:before { - content: "j"; + content: "\6a"; } .icon-like:before { - content: "k"; + content: "\6b"; } .icon-check:before { - content: "l"; + content: "\6c"; } .icon-edit:before { - content: "m"; -} -.icon-star:before { - content: "n"; + content: "\6d"; } .icon-user:before { - content: "o"; + content: "\6f"; } .icon-settings:before { - content: "q"; + content: "\71"; } .icon-stats:before { - content: "r"; + content: "\72"; } .icon-proposals:before { - content: "h"; + content: "\68"; } .icon-organizations:before { - content: "s"; + content: "\73"; } .icon-deleted:before { - content: "t"; + content: "\74"; } .icon-tag:before { - content: "u"; + content: "\75"; } .icon-eye:before { - content: "p"; + content: "\70"; } .icon-x:before { - content: "v"; + content: "\76"; } .icon-flag:before { - content: "w"; -} -.icon-notification:before { - content: "x"; + content: "\77"; } .icon-comment:before { - content: "y"; + content: "\79"; } .icon-reply:before { - content: "z"; + content: "\7a"; } .icon-facebook:before { - content: "A"; + content: "\41"; } .icon-google-plus:before { - content: "B"; -} -.icon-language:before { - content: "C"; + content: "\42"; } .icon-search:before { - content: "E"; + content: "\45"; } .icon-external:before { - content: "F"; + content: "\46"; } .icon-video:before { - content: "D"; + content: "\44"; } .icon-document:before { - content: "G"; + content: "\47"; } .icon-print:before { - content: "H"; + content: "\48"; } .icon-blog:before { - content: "J"; + content: "\4a"; } .icon-box:before { - content: "I"; + content: "\49"; } .icon-youtube:before { - content: "K"; + content: "\4b"; } .icon-letter:before { - content: "L"; -} \ No newline at end of file + content: "\4c"; +} +.icon-no-notification:before { + content: "\78"; +} +.icon-notification:before { + content: "\6e"; +} +.icon-circle:before { + content: "\43"; +} +.icon-circle-o:before { + content: "\4d"; +} diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 3685e0b6f..650621624 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -396,7 +396,6 @@ header { &:hover { background: none; color: white; - text-decoration: underline; transition: text-decoration 275ms; } @@ -420,6 +419,7 @@ header { &:hover, &:focus { background-color: #007095 !important; + text-decoration: underline; } } @@ -947,6 +947,77 @@ img.avatar, img.admin-avatar, img.moderator-avatar, img.initialjs-avatar { } } +.notifications { + position: relative; + + &:hover { + text-decoration: none; + } + + [class^="icon-"] { + font-size: $h4-font-size; + vertical-align: middle; + } + + .icon-circle { + color: #ecf00b; + font-size: $tiny-font-size; + position: absolute; + right: 4px; + top: -6px; + } +} + +.notifications-list:before { + background: $border; + content: ''; + height: 100%; + left: 28px; + position: absolute; + top: 0; + width: 2px; +} + +.notification { + display: block; + padding: $line-height/2 0 $line-height/2 $line-height*1.5; + position: relative; + + &:hover { + + a { + text-decoration: none; + } + + p:not(.time) { + color: $link; + } + + &:before { + content: "\43"; + } + } + + &:before { + background: white; + color: $brand; + content: "\4d"; + font-family: "icons" !important; + left: 6px; + position: absolute; + } + + p { + color: $text; + margin-bottom: 0; + } + + .time { + font-size: $small-font-size; + color: $text-medium; + } +} + // 09. Filters & search // - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/assets/stylesheets/variables.scss b/app/assets/stylesheets/variables.scss index d9da9c580..6a61ec72c 100644 --- a/app/assets/stylesheets/variables.scss +++ b/app/assets/stylesheets/variables.scss @@ -39,6 +39,7 @@ $h6-font-size: rem-calc(13); $h6-line-height: rem-calc(17); $small-font-size: rem-calc(14); +$tiny-font-size: rem-calc(10); $line-height: rem-calc(24); // 02. Colors diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 841b32498..5150f97ec 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -108,4 +108,5 @@ class ApplicationController < ActionController::Base store_location_for(:user, request.path) end end + end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 050962414..df928aa02 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -9,6 +9,7 @@ class CommentsController < ApplicationController def create if @comment.save CommentNotifier.new(comment: @comment).process + add_notification @comment else render :new end @@ -67,4 +68,13 @@ class CommentsController < ApplicationController ["1", true].include?(comment_params[:as_moderator]) && can?(:comment_as_moderator, @commentable) end + def add_notification(comment) + if comment.reply? + notifiable = comment.parent + else + notifiable = comment.commentable + end + Notification.add(notifiable.author_id, notifiable) unless comment.author_id == notifiable.author_id + end + end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb new file mode 100644 index 000000000..a4ec31b50 --- /dev/null +++ b/app/controllers/notifications_controller.rb @@ -0,0 +1,26 @@ +class NotificationsController < ApplicationController + before_action :authenticate_user! + after_action :mark_as_read, only: :show + skip_authorization_check + + def index + @notifications = current_user.notifications.unread.recent.for_render + end + + def show + @notification = current_user.notifications.find(params[:id]) + redirect_to url_for(@notification.notifiable) + end + + def mark_all_as_read + current_user.notifications.each { |notification| notification.mark_as_read } + redirect_to notifications_path + end + + private + + def mark_as_read + @notification.mark_as_read + end + +end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb new file mode 100644 index 000000000..281163380 --- /dev/null +++ b/app/helpers/notifications_helper.rb @@ -0,0 +1,6 @@ +module NotificationsHelper + + def notification_action(notification) + notification.notifiable_type == "Comment" ? "replies_to" : "comments_on" + end +end diff --git a/app/models/activity.rb b/app/models/activity.rb index 977204669..047ccb7dd 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -1,5 +1,4 @@ class Activity < ActiveRecord::Base - belongs_to :actionable, -> { with_hidden }, polymorphic: true belongs_to :user, -> { with_hidden } @@ -24,5 +23,4 @@ class Activity < ActiveRecord::Base def self.by(user) where(user: user) end - end diff --git a/app/models/notification.rb b/app/models/notification.rb new file mode 100644 index 000000000..1cb500ccf --- /dev/null +++ b/app/models/notification.rb @@ -0,0 +1,24 @@ +class Notification < ActiveRecord::Base + belongs_to :user, counter_cache: true + belongs_to :notifiable, polymorphic: true + + scope :unread, -> { all } + scope :recent, -> { order(id: :desc) } + scope :for_render, -> { includes(:notifiable) } + + def timestamp + notifiable.created_at + end + + def mark_as_read + self.destroy + end + + def self.add(user_id, notifiable) + if notification = Notification.find_by(user_id: user_id, notifiable: notifiable) + Notification.increment_counter(:counter, notification.id) + else + Notification.create!(user_id: user_id, notifiable: notifiable) + end + end +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index dd2bc5cf0..276598bf2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -22,6 +22,7 @@ class User < ActiveRecord::Base has_many :proposals, -> { with_hidden }, foreign_key: :author_id has_many :comments, -> { with_hidden } has_many :failed_census_calls + has_many :notifications validates :username, presence: true, if: :username_required? validates :username, uniqueness: true, if: :username_required? @@ -199,7 +200,7 @@ class User < ActiveRecord::Base def email_required? !erased? end - + def has_official_email? domain = Setting.value_for 'email_domain_for_officials' !email.blank? && ( (email.end_with? "@#{domain}") || (email.end_with? ".#{domain}") ) diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index c001da74e..e8c49636b 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -67,7 +67,7 @@ <%= simple_format text_with_links comment.body %> -
+
<%= render 'comments/votes', comment: comment %> diff --git a/app/views/devise/menu/_login_items.html.erb b/app/views/devise/menu/_login_items.html.erb index b0998949f..7681619e1 100644 --- a/app/views/devise/menu/_login_items.html.erb +++ b/app/views/devise/menu/_login_items.html.erb @@ -1,5 +1,16 @@
    <% if user_signed_in? %> +
  • + <%= link_to notifications_path, class: "notifications" do %> + <% if current_user.notifications_count > 0 %> + + + + <% else %> + + <% end %> + <% end %> +
  • <%= link_to(t("layouts.header.my_activity_link"), user_path(current_user)) %>
  • diff --git a/app/views/notifications/_notification.html.erb b/app/views/notifications/_notification.html.erb new file mode 100644 index 000000000..28def42f7 --- /dev/null +++ b/app/views/notifications/_notification.html.erb @@ -0,0 +1,9 @@ +
  • + <%= link_to notification do %> +

    + <%= t("notifications.index.#{notification_action(notification)}", count: notification.counter) %> + <%= notification.notifiable.is_a?(Comment) ? notification.notifiable.commentable.title : notification.notifiable.title %> +

    +

    <%= l notification.timestamp, format: :datetime %>

    + <% end %> +
  • \ No newline at end of file diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb new file mode 100644 index 000000000..cc9170e48 --- /dev/null +++ b/app/views/notifications/index.html.erb @@ -0,0 +1,18 @@ +
    +
    + <% if @notifications.empty? %> +
    + <%= t("notifications.index.empty_notifications") %> +
    + <% else %> +
    + <%= link_to t("notifications.index.mark_all_as_read"), + mark_all_as_read_notifications_path, method: :put %> +
    + +
      + <%= render @notifications %> +
    + <% end %> +
    +
    diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 561ad3347..6b815cd31 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -127,6 +127,8 @@ ignore_unused: - 'proposals.index.select_order' - 'proposals.index.orders.*' - 'proposals.index.search_form.*' + - 'notifications.index.comments_on*' + - 'notifications.index.replies_to*' - 'helpers.page_entries_info.*' # kaminari - 'views.pagination.*' # kaminari # - '{devise,kaminari,will_paginate}.*' diff --git a/config/locales/en.yml b/config/locales/en.yml index b7788f953..97494fd81 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -29,6 +29,8 @@ en: more_information: "More information" debates: "Debates" proposals: "Proposals" + new_notifications: "You have %{count} new notifications" + no_notifications: "You don't have new notifications" footer: description: "This portal uses the %{consul} which is %{open_source}. From Madrid out into the world." open_source: "open-source software" @@ -309,6 +311,16 @@ en: user_permission_votes: "Participate on final voting" user_permission_verify: "To perform all the actions verify your account." user_permission_verify_info: "* Only for users on Madrid City Census." + notifications: + index: + mark_all_as_read: "Mark all as read" + empty_notifications: "You don't have new notifications." + comments_on: + one: "Someone commented on" + other: "There are %{count} new comments on" + replies_to: + one: "Someone replied to your comment on" + other: "There are %{count} new replies to your comment on" simple_captcha: placeholder: "Enter the text from the image" label: "Enter the text from the image in the box below" diff --git a/config/locales/es.yml b/config/locales/es.yml index 3c00b7dc1..7fb02c729 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -29,6 +29,8 @@ es: more_information: "Más información" debates: "Debates" proposals: "Propuestas" + new_notifications: "Tienes %{count} notificaciones nuevas" + no_notifications: "No tienes notificaciones nuevas" footer: description: "Este portal usa la %{consul} que es %{open_source}. De Madrid, para el mundo entero." open_source: "software libre" @@ -309,6 +311,16 @@ es: user_permission_votes: "Participar en las votaciones finales*" user_permission_verify: "Para poder realizar todas las acciones verifica tu cuenta." user_permission_verify_info: "* Sólo usuarios empadronados en el municipio de Madrid." + notifications: + index: + mark_all_as_read: "Marcar todas como leídas" + empty_notifications: "No tienes notificaciones nuevas." + comments_on: + one: "Hay un nuevo comentario en" + other: "Hay %{count} comentarios nuevos en" + replies_to: + one: "Hay una respuesta nueva a tu comentario en" + other: "Hay %{count} nuevas respuestas a tu comentario en" simple_captcha: placeholder: "Introduce el texto de la imagen" label: "Introduce el texto de la imagen en la siguiente caja" diff --git a/config/routes.rb b/config/routes.rb index 2da32d479..bfdecc59f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -74,6 +74,11 @@ Rails.application.routes.draw do resource :account, controller: "account", only: [:show, :update, :delete] do collection { get :erase } end + + resources :notifications, only: [:index, :show] do + collection { put :mark_all_as_read } + end + resource :verification, controller: "verification", only: [:show] scope module: :verification do diff --git a/db/migrate/20150928115005_create_notifications.rb b/db/migrate/20150928115005_create_notifications.rb new file mode 100644 index 000000000..42bbbf72e --- /dev/null +++ b/db/migrate/20150928115005_create_notifications.rb @@ -0,0 +1,9 @@ +class CreateNotifications < ActiveRecord::Migration + def change + create_table :notifications do |t| + t.belongs_to :user, index: true, foreign_key: true + t.belongs_to :activity, index: true, foreign_key: true + t.boolean :read, default: false + end + end +end diff --git a/db/migrate/20160105170113_merge_activities_and_notifications.rb b/db/migrate/20160105170113_merge_activities_and_notifications.rb new file mode 100644 index 000000000..fac5b213d --- /dev/null +++ b/db/migrate/20160105170113_merge_activities_and_notifications.rb @@ -0,0 +1,10 @@ +class MergeActivitiesAndNotifications < ActiveRecord::Migration + def change + change_table :notifications do |t| + t.remove :read + t.remove :activity_id + t.references :notifiable, polymorphic: true + end + end + +end diff --git a/db/migrate/20160108114750_add_counter_to_notifications.rb b/db/migrate/20160108114750_add_counter_to_notifications.rb new file mode 100644 index 000000000..7cf3c434c --- /dev/null +++ b/db/migrate/20160108114750_add_counter_to_notifications.rb @@ -0,0 +1,5 @@ +class AddCounterToNotifications < ActiveRecord::Migration + def change + add_column :notifications, :counter, :integer, default: 1 + end +end diff --git a/db/migrate/20160108133501_add_notifications_counter_cache_to_user.rb b/db/migrate/20160108133501_add_notifications_counter_cache_to_user.rb new file mode 100644 index 000000000..e106df6ae --- /dev/null +++ b/db/migrate/20160108133501_add_notifications_counter_cache_to_user.rb @@ -0,0 +1,5 @@ +class AddNotificationsCounterCacheToUser < ActiveRecord::Migration + def change + add_column :users, :notifications_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index ca180b297..162bd816e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20151215165824) do +ActiveRecord::Schema.define(version: 20160108133501) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -204,6 +204,15 @@ ActiveRecord::Schema.define(version: 20151215165824) do add_index "moderators", ["user_id"], name: "index_moderators_on_user_id", using: :btree + create_table "notifications", force: :cascade do |t| + t.integer "user_id" + t.integer "notifiable_id" + t.string "notifiable_type" + t.integer "counter", default: 1 + end + + add_index "notifications", ["user_id"], name: "index_notifications_on_user_id", using: :btree + create_table "organizations", force: :cascade do |t| t.integer "user_id" t.string "name", limit: 60 @@ -330,6 +339,7 @@ ActiveRecord::Schema.define(version: 20151215165824) do t.datetime "erased_at" t.boolean "public_activity", default: true t.boolean "newsletter", default: false + t.integer "notifications_count", default: 0 end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree @@ -404,5 +414,6 @@ ActiveRecord::Schema.define(version: 20151215165824) do add_foreign_key "identities", "users" add_foreign_key "locks", "users" add_foreign_key "moderators", "users" + add_foreign_key "notifications", "users" add_foreign_key "organizations", "users" end diff --git a/spec/factories.rb b/spec/factories.rb index 2c20000ca..6e910a214 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -290,4 +290,9 @@ FactoryGirl.define do sequence(:track_id) { |n| "#{n}" } end + factory :notification do + user + association :notifiable, factory: :proposal + end + end diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb new file mode 100644 index 000000000..1ffc6a854 --- /dev/null +++ b/spec/features/notifications_spec.rb @@ -0,0 +1,192 @@ +require 'rails_helper' + +feature "Notifications" do + let(:author) { create :user } + let(:user) { create :user } + let(:debate) { create :debate, author: author } + let(:proposal) { create :proposal, author: author } + + scenario "User commented on my debate", :js do + login_as user + visit debate_path debate + + fill_in "comment-body-debate_#{debate.id}", with: "I commented on your debate" + click_button "Publish comment" + within "#comments" do + expect(page).to have_content "I commented on your debate" + end + + logout + 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 + + 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 + + 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 + + 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 "mark as read" do + + scenario "mark a single notification as read" do + user = create :user + notification = create :notification, user: user + + login_as user + visit notifications_path + + expect(page).to have_css ".notification", count: 1 + + first(".notification a").click + visit notifications_path + + expect(page).to have_css ".notification", count: 0 + end + + scenario "mark all notifications as read" do + user = create :user + 2.times { create :notification, user: user } + + login_as user + visit notifications_path + + expect(page).to have_css ".notification", count: 2 + click_link "Mark all as read" + + expect(page).to have_css ".notification", count: 0 + expect(current_path).to eq(notifications_path) + end + + end + + scenario "no notifications" do + login_as user + visit notifications_path + + expect(page).to have_content "You don't have new notifications" + end + +end diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb new file mode 100644 index 000000000..ccb3b3a9d --- /dev/null +++ b/spec/helpers/notifications_helper_spec.rb @@ -0,0 +1,25 @@ +require 'rails_helper' + +describe NotificationsHelper do + + describe "#notification_action" do + let(:debate) { create :debate } + let(:debate_comment) { create :comment, commentable: debate } + + context "when action was comment on a debate" do + it "returns correct text when someone comments on your debate" do + notification = create :notification, notifiable: debate + expect(notification_action(notification)).to eq "comments_on" + end + end + + context "when action was comment on a debate" do + it "returns correct text when someone replies to your comment" do + notification = create :notification, notifiable: debate_comment + expect(notification_action(notification)).to eq "replies_to" + end + end + end + + +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 11a02400a..73b70fb42 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -128,4 +128,5 @@ describe Comment do expect(Comment.not_as_admin_or_moderator.first).to eq(comment1) end end + end diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb new file mode 100644 index 000000000..361211a51 --- /dev/null +++ b/spec/models/notification_spec.rb @@ -0,0 +1,50 @@ +require 'rails_helper' + +describe Notification do + + describe "#unread (scope)" do + it "returns only unread notifications" do + 2.times { create :notification } + expect(Notification.unread.size).to be 2 + end + end + + describe "#recent (scope)" do + it "returns notifications sorted by id descendant" do + old_notification = create :notification + new_notification = create :notification + + sorted_notifications = Notification.recent + expect(sorted_notifications.size).to be 2 + expect(sorted_notifications.first).to eq new_notification + expect(sorted_notifications.last).to eq old_notification + end + end + + describe "#for_render (scope)" do + it "returns notifications including notifiable and user" do + expect(Notification).to receive(:includes).with(:notifiable).exactly(:once) + Notification.for_render + end + end + + describe "#timestamp" do + it "returns the timestamp of the trackable object" do + comment = create :comment + notification = create :notification, notifiable: comment + + expect(notification.timestamp).to eq comment.created_at + end + end + + describe "#mark_as_read" do + it "destroys notification" do + notification = create :notification + expect(Notification.unread.size).to eq 1 + + notification.mark_as_read + expect(Notification.unread.size).to eq 0 + end + end + +end