diff --git a/app/controllers/communities_controller.rb b/app/controllers/communities_controller.rb index f28e03a05..5efa48f9a 100644 --- a/app/controllers/communities_controller.rb +++ b/app/controllers/communities_controller.rb @@ -22,4 +22,8 @@ class CommunitiesController < ApplicationController def load_topics @topics = @community.topics.send("sort_by_#{@order}").page(params[:page]) end + + def load_participants + @participants = @community.participants + end end diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index eaa699c37..f0e57a383 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -1,7 +1,9 @@ class TopicsController < ApplicationController include CommentableActions + include FlagActions - before_action :set_community + before_action :load_community + before_action :load_topic, only: [:show, :edit, :update] has_orders %w{most_voted newest oldest}, only: :show @@ -13,7 +15,6 @@ class TopicsController < ApplicationController def create @topic = Topic.new(topic_params.merge(author: current_user, community_id: params[:community_id])) - if @topic.save redirect_to community_path(@community), notice: I18n.t('flash.actions.create.topic') else @@ -22,20 +23,17 @@ class TopicsController < ApplicationController end def show - @topic = Topic.find(params[:id]) @commentable = @topic @comment_tree = CommentTree.new(@commentable, params[:page], @current_order) set_comment_flags(@comment_tree.comments) end def edit - @topic = Topic.find(params[:id]) end def update - @topic = Topic.find(params[:id]) if @topic.update(topic_params) - redirect_to community_path(@community), notice: t('topic.update.notice') + redirect_to community_path(@community), notice: t('flash.actions.update.topic') else render :edit end @@ -44,10 +42,14 @@ class TopicsController < ApplicationController private def topic_params - params.require(:topic).permit(:title, :community_id, :description_as_comment) + params.require(:topic).permit(:title, :description) end - def set_community + def load_community @community = Community.find(params[:community_id]) end + + def load_topic + @topic = Topic.find(params[:id]) + end end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb index 1936f843c..1ab6c9826 100644 --- a/app/helpers/comments_helper.rb +++ b/app/helpers/comments_helper.rb @@ -42,7 +42,7 @@ module CommentsHelper def commentable_path(comment) commentable = comment.commentable - + case comment.commentable_type when "Budget::Investment" budget_investment_path(commentable.budget_id, commentable) @@ -50,6 +50,8 @@ module CommentsHelper legislation_process_question_path(commentable.process, commentable) when "Legislation::Annotation" legislation_process_draft_version_annotation_path(commentable.draft_version.process, commentable.draft_version, commentable) + when "Topic" + community_topic_path(comment.commentable.community, comment.commentable) else commentable end diff --git a/app/helpers/topics_helper.rb b/app/helpers/topics_helper.rb new file mode 100644 index 000000000..1d5f1964f --- /dev/null +++ b/app/helpers/topics_helper.rb @@ -0,0 +1,11 @@ +module TopicsHelper + + def disabled_create_topic + "disabled" unless current_user + end + + def disabled_info_title + t("community.show.sidebar.disabled_info_title") unless current_user + end + +end diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index b8f80c0cd..db4cee09d 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -33,7 +33,7 @@ module Abilities can :unmark_featured, Debate can :comment_as_administrator, [Debate, Comment, Proposal, Poll::Question, Budget::Investment, - Legislation::Question, Legislation::Annotation] + Legislation::Question, Legislation::Annotation, Topic] can [:search, :create, :index, :destroy], ::Administrator can [:search, :create, :index, :destroy], ::Moderator diff --git a/app/models/abilities/moderator.rb b/app/models/abilities/moderator.rb index 796bb185a..4e1427c12 100644 --- a/app/models/abilities/moderator.rb +++ b/app/models/abilities/moderator.rb @@ -6,7 +6,7 @@ module Abilities merge Abilities::Moderation.new(user) can :comment_as_moderator, [Debate, Comment, Proposal, Budget::Investment, Poll::Question, - Legislation::Question, Legislation::Annotation] + Legislation::Question, Legislation::Annotation, Topic] end end end diff --git a/app/models/community.rb b/app/models/community.rb index 5d8d82b34..feb6f9c59 100644 --- a/app/models/community.rb +++ b/app/models/community.rb @@ -3,4 +3,7 @@ class Community < ActiveRecord::Base has_one :investment has_many :topics + def participants + User.community_participants(self) + end end diff --git a/app/models/topic.rb b/app/models/topic.rb index 4161a4dce..8c8f3f6c9 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1,4 +1,6 @@ class Topic < ActiveRecord::Base + include Flaggable + acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases @@ -7,15 +9,12 @@ class Topic < ActiveRecord::Base has_many :comments, as: :commentable - after_create :associate_comment + validates :title, presence: true + validates :description, presence: true + validates :author, presence: true scope :sort_by_newest, -> { order(created_at: :desc) } scope :sort_by_oldest, -> { order(created_at: :asc) } scope :sort_by_most_commented, -> { reorder(comments_count: :desc) } - private - - def associate_comment - Comment.create(commentable: self, user: self.author, body: self.description_as_comment) - end end diff --git a/app/models/user.rb b/app/models/user.rb index 4d44456d6..da07d7fc7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -315,7 +315,13 @@ class User < ActiveRecord::Base def self.community_participants(community) topics_ids = community.topics.pluck(:id) - User.joins(:comments).where("comments.commentable_id IN (?) and comments.commentable_type = 'Topic'", topics_ids) + users_who_commented = User.joins(:comments).where("comments.commentable_id IN (?) and comments.commentable_type = 'Topic'", topics_ids).uniq + + author_ids = community.topics.pluck(:author_id) + users_who_authors = User.where("users.id IN (?)", author_ids) + + users_participants = users_who_commented + users_who_authors + users_participants.uniq end private diff --git a/app/views/comments/show.html.erb b/app/views/comments/show.html.erb index 37d379d4f..a779d1dfa 100644 --- a/app/views/comments/show.html.erb +++ b/app/views/comments/show.html.erb @@ -11,4 +11,4 @@ <%= render @comment %> - \ No newline at end of file + diff --git a/app/views/communities/show.html.erb b/app/views/communities/show.html.erb index f5009c92c..1720c1792 100644 --- a/app/views/communities/show.html.erb +++ b/app/views/communities/show.html.erb @@ -4,7 +4,7 @@
<%= @community.proposal.title %>
-<%= t("community.show.description") %>
+<%= t("community.show.description") %>
<%= t("community.topic.show.community_of_the_proposal") %> <%= @community.proposal.title %>
<%= @topic.description %>
<%= render '/shared/author_info', resource: @topic %> • diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index fde4d197f..3986b1b89 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -37,6 +37,7 @@ data: - config/locales/%{locale}/officing.yml - config/locales/%{locale}/budgets.yml - config/locales/%{locale}/legislation.yml + - config/locales/%{locale}/community.yml - config/locales/%{locale}/documents.yml # Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom: diff --git a/config/locales/en/community.yml b/config/locales/en/community.yml index fb6b98dd6..8298400a4 100644 --- a/config/locales/en/community.yml +++ b/config/locales/en/community.yml @@ -1,41 +1,48 @@ en: community: - create_first_community_theme: Create the first community theme - join_the_community_of_users: Join the community of users, give your opinion. - tab: - participant: Participants - sidebar: - title: Community - description: Join the community of users, give your opinion. + title: Comunidad + description: Partecipa a la comunidad de usuarios, da tu opinión. button_to_access: Access the community - - topic: - title: Participants - new_topic: Create topic - edit_topic: Edit theme - recommendations_title: Recommendations to create a theme - recommendation_one: Do not write the topic title or whole sentences in capital letters. On the internet that is considered shouting. And no one likes to be yelled at. - recommendation_two: Any topic or comment that implies an illegal action will be eliminated, also those that intend to sabotage the spaces of the subject, everything else is allowed. - recommendation_three: Enjoy this space, the voices that fill it, it's yours too - without_topics: Create the first community topic - show: - title: User community + title: Proposal community description: Participate in the community of this proposal - participants: Participants - + create_first_community_topic: Create the first community topic + tab: + participants: Participants + sidebar: + participate: Participate + new_topic: Create topic + disabled_info_title: You need to be logged to create a new topic + topic: + edit_button: Edit + comments: + one: 1 comment + other: "%{count} comments" + zero: No comments topic: - create: Create a theme - edit_button: Edit + create: Create a topic + edit: Edit Topic form: topic_title: Topic Title - topic_description_as_comment: Initial comment + topic_description: Description new: - submit_button: Create theme + submit_button: Create topic edit: - submit_button: Edit theme + submit_button: Edit topic + create: + submit_button: Create topic + update: + submit_button: Update topic show: community_of_the_proposal: Community of the proposal tab: comments_tab: Comments + sidebar: + recommendations_title: Recommendations to create a topic + recommendation_one: Do not write the topic title or whole sentences in capital letters. On the internet that is considered shouting. And no one likes to be yelled at. + recommendation_two: Any topic or comment that implies an illegal action will be eliminated, also those that intend to sabotage the spaces of the subject, everything else is allowed. + recommendation_three: Enjoy this space, the voices that fill it, it's yours too. + topics: + show: + login_to_comment: You must %{signin} or %{signup} to leave a comment. diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 9248fe457..93075d3dc 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -179,6 +179,7 @@ en: verification/sms: phone signature_sheet: Signature sheet document: Document + topic: Topic geozones: none: All city all: All scopes diff --git a/config/locales/en/responders.yml b/config/locales/en/responders.yml index 099751345..17de27b74 100644 --- a/config/locales/en/responders.yml +++ b/config/locales/en/responders.yml @@ -13,6 +13,7 @@ en: spending_proposal: "Spending proposal created successfully. You can access it from %{activity}" budget_investment: "Budget Investment created successfully." signature_sheet: "Signature sheet created successfully" + topic: "Topic created successfully." save_changes: notice: Changes saved update: @@ -23,6 +24,7 @@ en: proposal: "Proposal updated successfully." spending_proposal: "Investment project updated succesfully." budget_investment: "Investment project updated succesfully." + topic: "Topic updated successfully." destroy: spending_proposal: "Spending proposal deleted succesfully." budget_investment: "Investment project deleted succesfully." diff --git a/config/locales/es/community.yml b/config/locales/es/community.yml index 051b23763..347cf7dc9 100644 --- a/config/locales/es/community.yml +++ b/config/locales/es/community.yml @@ -1,39 +1,48 @@ es: community: - create_first_community_theme: Crea el primer tema de la comunidad - join_the_community_of_users: Partecipa a la comunidad de usuarios, da tu opinión. - tab: - participant: Participantes - sidebar: title: Comunidad description: Partecipa a la comunidad de usuarios, da tu opinión. button_to_access: Acceder a la comunidad - topic: - title: Participa - new_topic: Crea un tema - recommendations_title: Recomendaciones para crear un tema - recommendation_one: No escribas el título del tema o frases enteras en mayúsculas. En internet eso se considera gritar. Y a nadie le gusta que le griten. - recommendation_two: Cualquier tema o comentario que implique una acción ilegal será eliminada, también las que tengan la intención de sabotear los espacios del tema, todo lo demás está permitido. - recommendation_three: Disfruta de este espacio, de las voces que lo llenan, también es tuyo - without_topics: Crea el primer tema de la comunidad - show: - title: Comunidad de usuarios + title: Comunidad de la propuesta description: Participa en la comunidad de esta propuesta - paricipants: Participantes - + create_first_community_topic: Crea el primer tema de la comunidad + tab: + paricipants: Participantes + sidebar: + participate: Participa + new_topic: Crea un tema + disabled_info_title: Necesitas estar logueado para crear un nuevo tema + topic: + edit_button: Editar + comments: + one: 1 Comentario + other: "%{count} Comentarios" + zero: Sin comentarios topic: create: Crear un tema - edit_button: Editar + edit: Editar tema form: topic_title: Titulo del tema - topic_description_as_comment: Comentario inicial + topic_description: Descripción new: submit_button: Crear tema edit: submit_button: Guardar cambios + create: + submit_button: Crear tema + update: + submit_button: Actualizar tema show: community_of_the_proposal: Comunidad de la propuesta tab: comments_tab: Comentarios + sidebar: + recommendations_title: Recomendaciones para crear un tema + recommendation_one: No escribas el título del tema o frases enteras en mayúsculas. En internet eso se considera gritar. Y a nadie le gusta que le griten. + recommendation_two: Cualquier tema o comentario que implique una acción ilegal será eliminada, también las que tengan la intención de sabotear los espacios del tema, todo lo demás está permitido. + recommendation_three: Disfruta de este espacio, de las voces que lo llenan, también es tuyo + topics: + show: + login_to_comment: Necesitas %{signin} o %{signup} para comentar. diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 392a6dbfa..18597d347 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -179,6 +179,7 @@ es: verification/sms: el teléfono signature_sheet: la hoja de firmas document: el documento + topic: Tema geozones: none: Toda la ciudad all: Todos los ámbitos de actuación diff --git a/config/locales/es/responders.yml b/config/locales/es/responders.yml index 166809af9..d3d4ff163 100644 --- a/config/locales/es/responders.yml +++ b/config/locales/es/responders.yml @@ -13,6 +13,7 @@ es: spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}" budget_investment: "Propuesta de inversión creada correctamente." signature_sheet: "Hoja de firmas creada correctamente" + topic: "Tema creado correctamente." save_changes: notice: Cambios guardados update: @@ -23,6 +24,7 @@ es: poll_booth: "Urna actualizada correctamente." spending_proposal: "Propuesta de inversión actualizada correctamente." budget_investment: "Propuesta de inversión actualizada correctamente" + topic: "Tema actualizado correctamente." destroy: spending_proposal: "Propuesta de inversión eliminada." budget_investment: "Propuesta de inversión eliminada." diff --git a/db/migrate/20170807082243_create_topics.rb b/db/migrate/20170807082243_create_topics.rb index 1352751cc..073a65efc 100644 --- a/db/migrate/20170807082243_create_topics.rb +++ b/db/migrate/20170807082243_create_topics.rb @@ -2,7 +2,7 @@ class CreateTopics < ActiveRecord::Migration def change create_table :topics do |t| t.string :title, null: false - t.text :description_as_comment + t.text :description t.integer :author_id t.integer "comments_count", default: 0 t.references :community, index: true diff --git a/db/schema.rb b/db/schema.rb index 77b9d5d44..42c137cae 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -891,14 +891,14 @@ ActiveRecord::Schema.define(version: 20170807082243) do add_index "tags", ["spending_proposals_count"], name: "index_tags_on_spending_proposals_count", using: :btree create_table "topics", force: :cascade do |t| - t.string "title", null: false - t.text "description_as_comment" + t.string "title", null: false + t.text "description" t.integer "author_id" - t.integer "comments_count", default: 0 + t.integer "comments_count", default: 0 t.integer "community_id" t.datetime "hidden_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "topics", ["community_id"], name: "index_topics_on_community_id", using: :btree diff --git a/spec/factories.rb b/spec/factories.rb index 7b1f6a417..2817dfa2d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -784,4 +784,11 @@ LOREM_IPSUM locale "en" body "Some top links content" end + + factory :topic do + sequence(:title) { |n| "Topic title #{n}" } + sequence(:description) { |n| "Description as comment #{n}" } + association :author, factory: :user + end + end diff --git a/spec/features/comments/proposals_spec.rb b/spec/features/comments/proposals_spec.rb index 173dad2e6..24d1024b7 100644 --- a/spec/features/comments/proposals_spec.rb +++ b/spec/features/comments/proposals_spec.rb @@ -31,7 +31,7 @@ feature 'Commenting proposals' do expect(page).to have_content parent_comment.body expect(page).to have_content first_child.body expect(page).to have_content second_child.body - + debugger expect(page).to have_link "Go back to #{proposal.title}", href: proposal_path(proposal) end diff --git a/spec/features/comments/topics_spec.rb b/spec/features/comments/topics_spec.rb new file mode 100644 index 000000000..6c4c36d16 --- /dev/null +++ b/spec/features/comments/topics_spec.rb @@ -0,0 +1,548 @@ +require 'rails_helper' +include ActionView::Helpers::DateHelper + +feature 'Commenting topics' do + let(:user) { create :user } + let(:proposal) { create :proposal } + + scenario 'Index', :js do + + community = proposal.community + topic = create(:topic, community: community) + 3.times { create(:comment, commentable: topic) } + + visit community_topic_path(community, topic) + + expect(page).to have_css('.comment', count: 3) + + comment = Comment.last + within first('.comment') do + expect(page).to have_content comment.user.name + expect(page).to have_content I18n.l(comment.created_at, format: :datetime) + expect(page).to have_content comment.body + end + end + + scenario 'Show', :js do + community = proposal.community + topic = create(:topic, community: community) + parent_comment = create(:comment, commentable: topic) + first_child = create(:comment, commentable: topic, parent: parent_comment) + second_child = create(:comment, commentable: topic, parent: parent_comment) + + visit comment_path(parent_comment) + + expect(page).to have_css(".comment", count: 3) + expect(page).to have_content parent_comment.body + expect(page).to have_content first_child.body + expect(page).to have_content second_child.body + + expect(page).to have_link "Go back to #{topic.title}", href: community_topic_path(community, topic) + end + + scenario 'Collapsable comments', :js do + community = proposal.community + topic = create(:topic, community: community) + parent_comment = create(:comment, body: "Main comment", commentable: topic) + child_comment = create(:comment, body: "First subcomment", commentable: topic, parent: parent_comment) + grandchild_comment = create(:comment, body: "Last subcomment", commentable: topic, parent: child_comment) + + visit community_topic_path(community, topic) + + expect(page).to have_css('.comment', count: 3) + + find("#comment_#{child_comment.id}_children_arrow").trigger('click') + + expect(page).to have_css('.comment', count: 2) + expect(page).to_not have_content grandchild_comment.body + + find("#comment_#{child_comment.id}_children_arrow").trigger('click') + + expect(page).to have_css('.comment', count: 3) + expect(page).to have_content grandchild_comment.body + + find("#comment_#{parent_comment.id}_children_arrow").trigger('click') + + expect(page).to have_css('.comment', count: 1) + expect(page).to_not have_content child_comment.body + expect(page).to_not have_content grandchild_comment.body + end + + scenario 'Comment order' do + community = proposal.community + topic = create(:topic, community: community) + c1 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 100, + cached_votes_total: 120, created_at: Time.current - 2) + c2 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 10, + cached_votes_total: 12, created_at: Time.current - 1) + c3 = create(:comment, :with_confidence_score, commentable: topic, cached_votes_up: 1, + cached_votes_total: 2, created_at: Time.current) + + visit community_topic_path(community, topic, order: :most_voted) + + expect(c1.body).to appear_before(c2.body) + expect(c2.body).to appear_before(c3.body) + + visit community_topic_path(community, topic, order: :newest) + + expect(c3.body).to appear_before(c2.body) + expect(c2.body).to appear_before(c1.body) + + visit community_topic_path(community, topic, order: :oldest) + + expect(c1.body).to appear_before(c2.body) + expect(c2.body).to appear_before(c3.body) + end + + scenario 'Creation date works differently in roots and in child comments, when sorting by confidence_score' do + community = proposal.community + topic = create(:topic, community: community) + old_root = create(:comment, commentable: topic, created_at: Time.current - 10) + new_root = create(:comment, commentable: topic, created_at: Time.current) + old_child = create(:comment, commentable: topic, parent_id: new_root.id, created_at: Time.current - 10) + new_child = create(:comment, commentable: topic, parent_id: new_root.id, created_at: Time.current) + + visit community_topic_path(community, topic, order: :most_voted) + + expect(new_root.body).to appear_before(old_root.body) + expect(old_child.body).to appear_before(new_child.body) + + visit community_topic_path(community, topic, order: :newest) + + expect(new_root.body).to appear_before(old_root.body) + expect(new_child.body).to appear_before(old_child.body) + + visit community_topic_path(community, topic, order: :oldest) + + expect(old_root.body).to appear_before(new_root.body) + expect(old_child.body).to appear_before(new_child.body) + end + + scenario 'Turns links into html links' do + community = proposal.community + topic = create(:topic, community: community) + create :comment, commentable: topic, body: 'Built with http://rubyonrails.org/' + + visit community_topic_path(community, topic) + + within first('.comment') do + expect(page).to have_content 'Built with http://rubyonrails.org/' + expect(page).to have_link('http://rubyonrails.org/', href: 'http://rubyonrails.org/') + expect(find_link('http://rubyonrails.org/')[:rel]).to eq('nofollow') + expect(find_link('http://rubyonrails.org/')[:target]).to eq('_blank') + end + end + + scenario 'Sanitizes comment body for security' do + community = proposal.community + topic = create(:topic, community: community) + create :comment, commentable: topic, + body: " click me http://www.url.com" + + visit community_topic_path(community, topic) + + within first('.comment') do + expect(page).to have_content "click me http://www.url.com" + expect(page).to have_link('http://www.url.com', href: 'http://www.url.com') + expect(page).not_to have_link('click me') + end + end + + scenario 'Paginated comments' do + community = proposal.community + topic = create(:topic, community: community) + per_page = 10 + (per_page + 2).times { create(:comment, commentable: topic)} + + visit community_topic_path(community, topic) + + expect(page).to have_css('.comment', count: per_page) + within("ul.pagination") do + expect(page).to have_content("1") + expect(page).to have_content("2") + expect(page).to_not have_content("3") + click_link "Next", exact: false + end + + expect(page).to have_css('.comment', count: 2) + end + + feature 'Not logged user' do + scenario 'can not see comments forms' do + community = proposal.community + topic = create(:topic, community: community) + create(:comment, commentable: topic) + + visit community_topic_path(community, topic) + + expect(page).to have_content 'You must Sign in or Sign up to leave a comment' + within('#comments') do + expect(page).to_not have_content 'Write a comment' + expect(page).to_not have_content 'Reply' + end + end + end + + scenario 'Create', :js do + login_as(user) + community = proposal.community + topic = create(:topic, community: community) + visit community_topic_path(community, topic) + + fill_in "comment-body-topic_#{topic.id}", with: 'Have you thought about...?' + click_button 'Publish comment' + + within "#comments" do + expect(page).to have_content 'Have you thought about...?' + end + + within "#tab-comments-label" do + expect(page).to have_content 'Comments (1)' + end + end + + scenario 'Errors on create', :js do + login_as(user) + community = proposal.community + topic = create(:topic, community: community) + visit community_topic_path(community, topic) + + click_button 'Publish comment' + + expect(page).to have_content "Can't be blank" + end + + scenario 'Reply', :js do + community = proposal.community + topic = create(:topic, community: community) + citizen = create(:user, username: 'Ana') + manuela = create(:user, username: 'Manuela') + comment = create(:comment, commentable: topic, user: citizen) + + login_as(manuela) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: 'It will be done next week.' + click_button 'Publish reply' + end + + within "#comment_#{comment.id}" do + expect(page).to have_content 'It will be done next week.' + end + + expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) + end + + scenario 'Errors on reply', :js do + community = proposal.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic, user: user) + + login_as(user) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + click_button 'Publish reply' + expect(page).to have_content "Can't be blank" + end + + end + + scenario "N replies", :js do + community = proposal.community + topic = create(:topic, community: community) + parent = create(:comment, commentable: topic) + + 7.times do + create(:comment, commentable: topic, parent: parent) + parent = parent.children.first + end + + visit community_topic_path(community, topic) + expect(page).to have_css(".comment.comment.comment.comment.comment.comment.comment.comment") + end + + scenario "Flagging as inappropriate", :js do + community = proposal.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic) + + login_as(user) + visit community_topic_path(community, topic) + + within "#comment_#{comment.id}" do + page.find("#flag-expand-comment-#{comment.id}").click + page.find("#flag-comment-#{comment.id}").click + + expect(page).to have_css("#unflag-expand-comment-#{comment.id}") + end + + expect(Flag.flagged?(user, comment)).to be + end + + scenario "Undoing flagging as inappropriate", :js do + community = proposal.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic) + Flag.flag(user, comment) + + login_as(user) + visit community_topic_path(community, topic) + + within "#comment_#{comment.id}" do + page.find("#unflag-expand-comment-#{comment.id}").click + page.find("#unflag-comment-#{comment.id}").click + + expect(page).to have_css("#flag-expand-comment-#{comment.id}") + end + + expect(Flag.flagged?(user, comment)).to_not be + end + + scenario "Flagging turbolinks sanity check", :js do + community = proposal.community + topic = create(:topic, community: community, title: "Should we change the world?") + comment = create(:comment, commentable: topic) + + login_as(user) + visit community_path(community) + click_link "Should we change the world?" + + within "#comment_#{comment.id}" do + page.find("#flag-expand-comment-#{comment.id}").click + expect(page).to have_selector("#flag-comment-#{comment.id}") + end + end + + scenario "Erasing a comment's author" do + community = proposal.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic, body: "this should be visible") + comment.user.erase + + visit community_topic_path(community, topic) + + within "#comment_#{comment.id}" do + expect(page).to have_content('User deleted') + expect(page).to have_content('this should be visible') + end + end + + feature "Moderators" do + scenario "can create comment as a moderator", :js do + community = proposal.community + topic = create(:topic, community: community) + moderator = create(:moderator) + + login_as(moderator.user) + visit community_topic_path(community, topic) + + fill_in "comment-body-topic_#{topic.id}", with: "I am moderating!" + check "comment-as-moderator-topic_#{topic.id}" + click_button "Publish comment" + + within "#comments" do + expect(page).to have_content "I am moderating!" + expect(page).to have_content "Moderator ##{moderator.id}" + expect(page).to have_css "div.is-moderator" + expect(page).to have_css "img.moderator-avatar" + end + end + + scenario "can create reply as a moderator", :js do + community = proposal.community + topic = create(:topic, community: community) + citizen = create(:user, username: "Ana") + manuela = create(:user, username: "Manuela") + moderator = create(:moderator, user: manuela) + comment = create(:comment, commentable: topic, user: citizen) + + login_as(manuela) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: "I am moderating!" + check "comment-as-moderator-comment_#{comment.id}" + click_button 'Publish reply' + end + + within "#comment_#{comment.id}" do + expect(page).to have_content "I am moderating!" + expect(page).to have_content "Moderator ##{moderator.id}" + expect(page).to have_css "div.is-moderator" + expect(page).to have_css "img.moderator-avatar" + end + + expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) + end + + scenario "can not comment as an administrator" do + community = proposal.community + topic = create(:topic, community: community) + moderator = create(:moderator) + + login_as(moderator.user) + visit community_topic_path(community, topic) + + expect(page).to_not have_content "Comment as administrator" + end + end + + feature "Administrators" do + scenario "can create comment as an administrator", :js do + community = proposal.community + topic = create(:topic, community: community) + admin = create(:administrator) + + login_as(admin.user) + visit community_topic_path(community, topic) + + fill_in "comment-body-topic_#{topic.id}", with: "I am your Admin!" + check "comment-as-administrator-topic_#{topic.id}" + click_button "Publish comment" + + within "#comments" do + expect(page).to have_content "I am your Admin!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + end + + scenario "can create reply as an administrator", :js do + community = proposal.community + topic = create(:topic, community: community) + citizen = create(:user, username: "Ana") + manuela = create(:user, username: "Manuela") + admin = create(:administrator, user: manuela) + comment = create(:comment, commentable: topic, user: citizen) + + login_as(manuela) + visit community_topic_path(community, topic) + + click_link "Reply" + + within "#js-comment-form-comment_#{comment.id}" do + fill_in "comment-body-comment_#{comment.id}", with: "Top of the world!" + check "comment-as-administrator-comment_#{comment.id}" + click_button 'Publish reply' + end + + within "#comment_#{comment.id}" do + expect(page).to have_content "Top of the world!" + expect(page).to have_content "Administrator ##{admin.id}" + expect(page).to have_css "div.is-admin" + expect(page).to have_css "img.admin-avatar" + end + + expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) + end + + scenario "can not comment as a moderator" do + community = proposal.community + topic = create(:topic, community: community) + admin = create(:administrator) + + login_as(admin.user) + visit community_topic_path(community, topic) + + expect(page).to_not have_content "Comment as moderator" + end + end + + feature 'Voting comments' do + + background do + @manuela = create(:user, verified_at: Time.current) + @pablo = create(:user) + @proposal = create(:proposal) + @topic = create(:topic, community: @proposal.community) + @comment = create(:comment, commentable: @topic) + + login_as(@manuela) + end + + scenario 'Show' do + create(:vote, voter: @manuela, votable: @comment, vote_flag: true) + create(:vote, voter: @pablo, votable: @comment, vote_flag: false) + + visit community_topic_path(@proposal.community, @topic) + + within("#comment_#{@comment.id}_votes") do + within(".in_favor") do + expect(page).to have_content "1" + end + + within(".against") do + expect(page).to have_content "1" + end + + expect(page).to have_content "2 votes" + end + end + + scenario 'Create', :js do + visit community_topic_path(@proposal.community, @topic) + + within("#comment_#{@comment.id}_votes") do + find(".in_favor a").click + + within(".in_favor") do + expect(page).to have_content "1" + end + + within(".against") do + expect(page).to have_content "0" + end + + expect(page).to have_content "1 vote" + end + end + + scenario 'Update', :js do + visit community_topic_path(@proposal.community, @topic) + + within("#comment_#{@comment.id}_votes") do + find('.in_favor a').click + find('.against a').click + + within('.in_favor') do + expect(page).to have_content "0" + end + + within('.against') do + expect(page).to have_content "1" + end + + expect(page).to have_content "1 vote" + end + end + + scenario 'Trying to vote multiple times', :js do + visit community_topic_path(@proposal.community, @topic) + + within("#comment_#{@comment.id}_votes") do + find('.in_favor a').click + find('.in_favor a').click + + within('.in_favor') do + expect(page).to have_content "1" + end + + within('.against') do + expect(page).to have_content "0" + end + + expect(page).to have_content "1 vote" + end + end + end + +end diff --git a/spec/features/communities_spec.rb b/spec/features/communities_spec.rb new file mode 100644 index 000000000..7ea966e75 --- /dev/null +++ b/spec/features/communities_spec.rb @@ -0,0 +1,107 @@ +require 'rails_helper' + +feature 'Communities' do + + context 'Show' do + + scenario 'Should display default content' do + proposal = create(:proposal) + community = proposal.community + user = create(:user) + login_as(user) + + visit community_path(community) + + expect(page).to have_content "Proposal community" + expect(page).to have_content proposal.title + expect(page).to have_content "Participate in the community of this proposal" + expect(page).to have_link("Create topic", href: new_community_topic_path(community)) + expect(page).not_to have_selector(".button.disabled", text: "Create topic") + end + + scenario 'Should display disabled create topic button when user is not logged' do + proposal = create(:proposal) + community = proposal.community + + visit community_path(community) + + expect(page).to have_selector(".button.disabled", text: "Create topic") + end + + scenario 'Should display without_topics_text and empty participants when there are not topics' do + proposal = create(:proposal) + community = proposal.community + + visit community_path(community) + + expect(page).to have_content "Create the first community topic" + expect(page).to have_content "Participants (0)" + end + + scenario 'Should display order selector and topic content when there are topics' do + proposal = create(:proposal) + community = proposal.community + topic = create(:topic, community: community) + create(:comment, commentable: topic) + + visit community_path(community) + + expect(page).to have_selector ".wide-order-selector" + within "#topic_#{topic.id}" do + expect(page).to have_content topic.title + expect(page).to have_content "#{topic.comments_count} comment" + expect(page).to have_content I18n.l(topic.created_at.to_date) + expect(page).to have_content topic.author.name + end + end + + scenario 'Should display topic edit button when author is logged' do + proposal = create(:proposal) + community = proposal.community + user = create(:user) + topic1 = create(:topic, community: community, author: user) + topic2 = create(:topic, community: community) + login_as(user) + + visit community_path(community) + + within "#topic_#{topic1.id}" do + expect(page).to have_link("Edit", href: edit_community_topic_path(community, topic1)) + end + + within "#topic_#{topic2.id}" do + expect(page).not_to have_link("Edit", href: edit_community_topic_path(community, topic2)) + end + end + + scenario 'Should display participant when there is topics' do + proposal = create(:proposal) + community = proposal.community + topic = create(:topic, community: community) + + visit community_path(community) + + within ".communities-participant" do + expect(page).to have_content "Participants (1)" + expect(page).to have_content topic.author.name + end + end + + scenario 'Should display participants when there are topics and comments' do + proposal = create(:proposal) + community = proposal.community + topic = create(:topic, community: community) + comment = create(:comment, commentable: topic) + + visit community_path(community) + + within ".communities-participant" do + expect(page).to have_content "Participants (2)" + expect(page).to have_content topic.author.name + expect(page).to have_content comment.author.name + end + end + + end + +end diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb index 3da21f98f..291de5022 100644 --- a/spec/features/proposals_spec.rb +++ b/spec/features/proposals_spec.rb @@ -63,6 +63,7 @@ feature 'Proposals' do expect(page.html).to include "
<%= t("community.topic.sidebar.recommendations_title") %>
++- <%= t("community.topic.sidebar.recommendation_one") %>
+ - <%= t("community.topic.sidebar.recommendation_two") %>
+ - <%= t("community.topic.sidebar.recommendation_three") %>
+
+<%= link_to topic.title, community_topic_path(@community, topic) %>
- <%= link_to t("proposals.proposal.comments", count: topic.comments_count), community_topic_path(@community, topic, anchor: "comments") %> + <%= link_to t("community.show.topic.comments", count: topic.comments_count), community_topic_path(@community, topic, anchor: "comments") %> • <%= I18n.l topic.created_at.to_date %> • @@ -20,11 +20,11 @@
<%= t("community.create_first_community_theme") %>
+<%= t("community.show.create_first_community_topic") %>
<% end %> diff --git a/app/views/topics/edit.html.erb b/app/views/topics/edit.html.erb index 9f443b5d9..1741d3cfa 100644 --- a/app/views/topics/edit.html.erb +++ b/app/views/topics/edit.html.erb @@ -2,17 +2,9 @@<%= t("topic.edit.editing") %>
+<%= t("community.topic.edit") %>
<%= render "form" %><%= t("community.sidebar.topic.recommendations_title") %>
--- <%= t("community.sidebar.topic.recommendation_one") %>
- - <%= t("community.sidebar.topic.recommendation_two") %>
- - <%= t("community.sidebar.topic..recommendation_three") %>
-
-<%= t("community.topic.create") %>
- - <%= render '/topics/form' %> - <%#= render '/topics/form', form_url: budget_investments_path(@budget) %> + <%= render 'form' %><%= t("community.sidebar.topic.recommendations_title") %>
--- <%= t("community.sidebar.topic.recommendation_one") %>
- - <%= t("community.sidebar.topic.recommendation_two") %>
- - <%= t("community.sidebar.topic..recommendation_three") %>
-
-