From d2cc110678be9c42a8bf12168290108e648edc86 Mon Sep 17 00:00:00 2001 From: decabeza Date: Mon, 29 Aug 2022 17:23:15 +0200 Subject: [PATCH 01/17] Use `this` instead of the CSS selector `.sortable` In this context, `this` already points to the element, so there is no need to search it again. --- app/assets/javascripts/sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/sortable.js b/app/assets/javascripts/sortable.js index 83a3d4721..ed085d5bd 100644 --- a/app/assets/javascripts/sortable.js +++ b/app/assets/javascripts/sortable.js @@ -9,7 +9,7 @@ attribute: "data-answer-id" }); $.ajax({ - url: $(".sortable").data("js-url"), + url: $(this).data("js-url"), data: { ordered_list: new_order }, From d1c1aa6691ada53f7af47d65abd52c8827eb2b35 Mon Sep 17 00:00:00 2001 From: decabeza Date: Tue, 30 Aug 2022 12:09:13 +0200 Subject: [PATCH 02/17] Add `VotationType` model --- app/models/concerns/questionable.rb | 12 +++++++ app/models/poll/question.rb | 1 + app/models/votation_type.rb | 17 +++++++++ .../20220323233643_add_votation_types.rb | 12 +++++++ db/schema.rb | 9 +++++ spec/factories/votation_type.rb | 14 ++++++++ spec/models/votation_type_spec.rb | 35 +++++++++++++++++++ 7 files changed, 100 insertions(+) create mode 100644 app/models/concerns/questionable.rb create mode 100644 app/models/votation_type.rb create mode 100644 db/migrate/20220323233643_add_votation_types.rb create mode 100644 spec/factories/votation_type.rb create mode 100644 spec/models/votation_type_spec.rb diff --git a/app/models/concerns/questionable.rb b/app/models/concerns/questionable.rb new file mode 100644 index 000000000..12d8ce399 --- /dev/null +++ b/app/models/concerns/questionable.rb @@ -0,0 +1,12 @@ +module Questionable + extend ActiveSupport::Concern + + included do + has_one :votation_type, as: :questionable, dependent: :destroy + delegate :max_votes, :multiple?, :vote_type, to: :votation_type, allow_nil: true + end + + def unique? + votation_type.nil? || votation_type.unique? + end +end diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 6f489c8d8..6be7aecc9 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -1,6 +1,7 @@ class Poll::Question < ApplicationRecord include Measurable include Searchable + include Questionable acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases diff --git a/app/models/votation_type.rb b/app/models/votation_type.rb new file mode 100644 index 000000000..00fca0ecf --- /dev/null +++ b/app/models/votation_type.rb @@ -0,0 +1,17 @@ +class VotationType < ApplicationRecord + belongs_to :questionable, polymorphic: true + + QUESTIONABLE_TYPES = %w[Poll::Question].freeze + + enum vote_type: %w[unique multiple] + + validates :questionable, presence: true + validates :questionable_type, inclusion: { in: ->(*) { QUESTIONABLE_TYPES }} + validates :max_votes, presence: true, if: :max_votes_required? + + private + + def max_votes_required? + multiple? + end +end diff --git a/db/migrate/20220323233643_add_votation_types.rb b/db/migrate/20220323233643_add_votation_types.rb new file mode 100644 index 000000000..beca11ef6 --- /dev/null +++ b/db/migrate/20220323233643_add_votation_types.rb @@ -0,0 +1,12 @@ +class AddVotationTypes < ActiveRecord::Migration[5.2] + def change + create_table :votation_types do |t| + t.integer :questionable_id + t.string :questionable_type + t.integer :vote_type + t.integer :max_votes + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index cb93e1c07..20c37835e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1698,6 +1698,15 @@ ActiveRecord::Schema.define(version: 2022_09_15_154808) do t.index ["user_id"], name: "index_visits_on_user_id" end + create_table "votation_types", force: :cascade do |t| + t.integer "questionable_id" + t.string "questionable_type" + t.integer "vote_type" + t.integer "max_votes" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "votes", id: :serial, force: :cascade do |t| t.string "votable_type" t.integer "votable_id" diff --git a/spec/factories/votation_type.rb b/spec/factories/votation_type.rb new file mode 100644 index 000000000..dfee71b8d --- /dev/null +++ b/spec/factories/votation_type.rb @@ -0,0 +1,14 @@ +FactoryBot.define do + factory :votation_type do + factory :votation_type_unique do + vote_type { "unique" } + end + + factory :votation_type_multiple do + vote_type { "multiple" } + max_votes { 3 } + end + + association :questionable, factory: :poll_question + end +end diff --git a/spec/models/votation_type_spec.rb b/spec/models/votation_type_spec.rb new file mode 100644 index 000000000..4c947ddd6 --- /dev/null +++ b/spec/models/votation_type_spec.rb @@ -0,0 +1,35 @@ +require "rails_helper" + +describe VotationType do + let(:vote_types) { %i[votation_type_unique votation_type_multiple] } + let(:votation_type) { build(vote_types.sample) } + + it "is valid" do + expect(votation_type).to be_valid + end + + it "is not valid without questionable" do + votation_type.questionable = nil + + expect(votation_type).not_to be_valid + end + + it "is not valid when questionable_type is not allowed" do + votation_type.questionable_type = Poll::Answer + + expect(votation_type).not_to be_valid + expect(votation_type.errors[:questionable_type]).to include "is not included in the list" + end + + it "is not valid when max_votes is undefined for multiple votation_type" do + votation_type.max_votes = nil + votation_type.vote_type = "unique" + + expect(votation_type).to be_valid + + votation_type.vote_type = "multiple" + + expect(votation_type).not_to be_valid + expect(votation_type.errors[:max_votes]).to include "can't be blank" + end +end From 815a526d78dcea2a3cc681f8a8a4de52f237d081 Mon Sep 17 00:00:00 2001 From: decabeza Date: Tue, 30 Aug 2022 13:13:33 +0200 Subject: [PATCH 03/17] Add `VotationType` fields to admin poll question form --- .../admin/votation_types/fields.js | 21 ++++++++++++++ app/assets/javascripts/application.js | 1 + .../poll/questions/form_component.html.erb | 8 ++++++ .../votation_types/fields_component.html.erb | 20 +++++++++++++ .../admin/votation_types/fields_component.rb | 7 +++++ .../admin/poll/questions_controller.rb | 5 ++-- app/models/concerns/questionable.rb | 1 + app/views/admin/poll/questions/show.html.erb | 15 ++++++++++ config/locales/en/activerecord.yml | 9 ++++++ config/locales/en/admin.yml | 4 +++ config/locales/es/activerecord.yml | 9 ++++++ config/locales/es/admin.yml | 4 +++ spec/system/admin/poll/questions_spec.rb | 28 +++++++++++++++++++ 13 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/admin/votation_types/fields.js create mode 100644 app/components/admin/votation_types/fields_component.html.erb create mode 100644 app/components/admin/votation_types/fields_component.rb diff --git a/app/assets/javascripts/admin/votation_types/fields.js b/app/assets/javascripts/admin/votation_types/fields.js new file mode 100644 index 000000000..85649e675 --- /dev/null +++ b/app/assets/javascripts/admin/votation_types/fields.js @@ -0,0 +1,21 @@ +(function() { + "use strict"; + App.AdminVotationTypesFields = { + adjustForm: function() { + if ($(this).val() === "unique") { + $(".max-votes").hide(); + $(".description-unique").show(); + $(".description-multiple").hide(); + $(".votation-type-max-votes").prop("disabled", true); + } else { + $(".max-votes").show(); + $(".description-unique").hide(); + $(".description-multiple").show(); + $(".votation-type-max-votes").prop("disabled", false); + } + }, + initialize: function() { + $(".votation-type-vote-type").on("change", App.AdminVotationTypesFields.adjustForm).trigger("change"); + } + }; +}).call(this); diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 52e178b4b..58f344977 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -167,6 +167,7 @@ var initialize_modules = function() { } App.AdminBudgetsWizardCreationStep.initialize(); App.AdminMachineLearningScripts.initialize(); + App.AdminVotationTypesFields.initialize(); App.BudgetEditAssociations.initialize(); App.BudgetHideMoney.initialize(); App.Datepicker.initialize(); diff --git a/app/components/admin/poll/questions/form_component.html.erb b/app/components/admin/poll/questions/form_component.html.erb index fc183f186..bd72edd40 100644 --- a/app/components/admin/poll/questions/form_component.html.erb +++ b/app/components/admin/poll/questions/form_component.html.erb @@ -29,6 +29,14 @@ <% end %> +
+
+ <%= f.fields_for :votation_type do |votation_f| %> + <%= render Admin::VotationTypes::FieldsComponent.new(form: votation_f) %> + <% end %> +
+
+
<%= f.submit(class: "button success expanded", value: t("shared.save")) %> diff --git a/app/components/admin/votation_types/fields_component.html.erb b/app/components/admin/votation_types/fields_component.html.erb new file mode 100644 index 000000000..f55d74579 --- /dev/null +++ b/app/components/admin/votation_types/fields_component.html.erb @@ -0,0 +1,20 @@ +
+ <%= form.enum_select :vote_type, {}, class: "votation-type-vote-type" %> +
+ +
+
+ + <%= t("admin.polls.votation_type.unique_description") %> + + +
+
+ +
+
+ <%= form.number_field :max_votes, min: 2, max: 999, class: "votation-type-max-votes" %> +
+
diff --git a/app/components/admin/votation_types/fields_component.rb b/app/components/admin/votation_types/fields_component.rb new file mode 100644 index 000000000..6fe487397 --- /dev/null +++ b/app/components/admin/votation_types/fields_component.rb @@ -0,0 +1,7 @@ +class Admin::VotationTypes::FieldsComponent < ApplicationComponent + attr_reader :form + + def initialize(form:) + @form = form + end +end diff --git a/app/controllers/admin/poll/questions_controller.rb b/app/controllers/admin/poll/questions_controller.rb index c0c8540f8..3981f652b 100644 --- a/app/controllers/admin/poll/questions_controller.rb +++ b/app/controllers/admin/poll/questions_controller.rb @@ -14,10 +14,10 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController end def new - @polls = Poll.all proposal = Proposal.find(params[:proposal_id]) if params[:proposal_id].present? @question.copy_attributes_from_proposal(proposal) @question.poll = @poll + @question.votation_type = VotationType.new authorize! :create, @question end @@ -58,8 +58,7 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController end def allowed_params - attributes = [:poll_id, :question, :proposal_id] - + attributes = [:poll_id, :question, :proposal_id, votation_type_attributes: [:vote_type, :max_votes]] [*attributes, translation_params(Poll::Question)] end diff --git a/app/models/concerns/questionable.rb b/app/models/concerns/questionable.rb index 12d8ce399..f7b20b8f4 100644 --- a/app/models/concerns/questionable.rb +++ b/app/models/concerns/questionable.rb @@ -3,6 +3,7 @@ module Questionable included do has_one :votation_type, as: :questionable, dependent: :destroy + accepts_nested_attributes_for :votation_type delegate :max_votes, :multiple?, :vote_type, to: :votation_type, allow_nil: true end diff --git a/app/views/admin/poll/questions/show.html.erb b/app/views/admin/poll/questions/show.html.erb index ec4e9cecc..f6c8ef32b 100644 --- a/app/views/admin/poll/questions/show.html.erb +++ b/app/views/admin/poll/questions/show.html.erb @@ -28,6 +28,21 @@ <%= link_to @question.proposal.title, proposal_path(@question.proposal) %>

<% end %> + + <% if @question.votation_type.present? %> +

+ <%= t("admin.polls.votation_type.title") %> +
+ <%= VotationType.human_attribute_name("vote_type.#{@question.vote_type}") %> +

+ <% if @question.max_votes.present? %> +

+ <%= VotationType.human_attribute_name("max_votes") %> +
+ <%= @question.max_votes %> +

+ <% end %> + <% end %>
diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index c9e4cd7c6..7b1311da8 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -145,6 +145,9 @@ en: local_census_record: one: Local census record other: Local census records + votation_type: + one: Votation type + other: Votation types attributes: budget: name: "Name" @@ -514,6 +517,12 @@ en: document_number: Document number date_of_birth: Date of birth postal_code: Postal code + votation_type: + max_votes: Maximum number of votes + vote_type: Votation type + votation_type/vote_type: + unique: Unique answer + multiple: Multiple answers errors: models: user: diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 20635ee3a..b1b68b4b8 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -1107,6 +1107,10 @@ en: alert: "This action will remove the poll and all its associated questions." success_notice: "Poll deleted successfully" unable_notice: "You cannot delete a poll that has votes" + votation_type: + title: "Votation type" + unique_description: "It's only possible to answer one time to the question." + multiple_description: "Allows to choose multiple answers. It's possible to set the maximum number of answers." questions: index: title: "Questions" diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index b7cf2fc07..99ce9c5b0 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -145,6 +145,9 @@ es: local_census_record: one: Registro del censo local other: Registros del censo local + votation_type: + one: Tipo de votación + other: Tipos de votación attributes: budget: name: "Nombre" @@ -514,6 +517,12 @@ es: document_number: Número de documento date_of_birth: Fecha de nacimiento postal_code: Código postal + votation_type: + max_votes: Número máximo de votos + vote_type: Tipo de votación + votation_type/vote_type: + unique: Respuesta única + multiple: Respuesta múltiple errors: models: user: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index d40af634b..08caffd41 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -1106,6 +1106,10 @@ es: alert: "Esta acción eliminará la votación y todas sus preguntas asociadas." success_notice: "Votación eliminada correctamente" unable_notice: "No se pueden eliminar votaciones con votos" + votation_type: + title: "Tipo de votación" + unique_description: "Solo se puede responder a la pregunta con una única respuesta." + multiple_description: "Permite elegir más de una respuesta. Se puede elegir el número máximo de respuestas." questions: index: title: "Preguntas de votaciones" diff --git a/spec/system/admin/poll/questions_spec.rb b/spec/system/admin/poll/questions_spec.rb index 99c81a34c..d6f559fd7 100644 --- a/spec/system/admin/poll/questions_spec.rb +++ b/spec/system/admin/poll/questions_spec.rb @@ -65,6 +65,34 @@ describe "Admin poll questions", :admin do expect(page).not_to have_link "Create question" end + + describe "With votation type" do + before do + poll = create(:poll, :future) + visit admin_poll_path(poll) + click_link "Create question" + end + + scenario "Unique" do + fill_in "Question", with: "Question with unique answer" + select "Unique answer", from: "Votation type" + + click_button "Save" + + expect(page).to have_content "Question with unique answer" + expect(page).to have_content "Unique answer" + end + + scenario "Multiple" do + fill_in "Question", with: "Question with multiple answers" + select "Multiple answers", from: "Votation type" + fill_in "Maximum number of votes", with: 6 + click_button "Save" + + expect(page).to have_content "Question with multiple answers" + expect(page).to have_content "Multiple answers" + end + end end scenario "Create from proposal" do From b92b38f48f320c855699fc7a69a94d610dba46c9 Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 1 Sep 2022 12:29:26 +0200 Subject: [PATCH 04/17] Show question title before answers additional information --- .../read_more_answer_component.html.erb | 111 +++++++++--------- .../questions/read_more_answer_component.rb | 8 +- app/controllers/polls_controller.rb | 2 - app/views/polls/show.html.erb | 2 +- .../read_more_answer_component_spec.rb | 23 +++- spec/system/polls/polls_spec.rb | 12 -- 6 files changed, 81 insertions(+), 77 deletions(-) diff --git a/app/components/polls/questions/read_more_answer_component.html.erb b/app/components/polls/questions/read_more_answer_component.html.erb index becece5a7..06b374a6d 100644 --- a/app/components/polls/questions/read_more_answer_component.html.erb +++ b/app/components/polls/questions/read_more_answer_component.html.erb @@ -1,60 +1,63 @@ -
" id="answer_<%= answer.id %>"> -

<%= answer.title %>

+

<%= question.title %>

+<% question.question_answers.each do |answer| %> +
" id="answer_<%= answer.id %>"> +

<%= answer.title %>

- <% if answer.images.any? %> - <%= render "polls/gallery", answer: answer %> - <% end %> + <% if answer.images.any? %> + <%= render "polls/gallery", answer: answer %> + <% end %> - <% if answer.description.present? %> -
-
- <%= wysiwyg(answer.description) %> + <% if answer.description.present? %> +
+
+ <%= wysiwyg(answer.description) %> +
+
+ + +
-
- - + <% end %> + + <% if answer.documents.present? %> + -
- <% end %> + <% end %> - <% if answer.documents.present? %> - + <% answer.videos.each do |video| %> + <%= link_to video.title, + video.url, + target: "_blank", + rel: "nofollow" %>
+ <% end %> +
+ <% end %> +
+<% end %> diff --git a/app/components/polls/questions/read_more_answer_component.rb b/app/components/polls/questions/read_more_answer_component.rb index edb34180c..a88801581 100644 --- a/app/components/polls/questions/read_more_answer_component.rb +++ b/app/components/polls/questions/read_more_answer_component.rb @@ -1,9 +1,9 @@ class Polls::Questions::ReadMoreAnswerComponent < ApplicationComponent - with_collection_parameter :answer - attr_reader :answer + with_collection_parameter :question + attr_reader :question delegate :wysiwyg, to: :helpers - def initialize(answer:) - @answer = answer + def initialize(question:) + @question = question end end diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index af2dc563a..0828fc9fb 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -19,8 +19,6 @@ class PollsController < ApplicationController def show @questions = @poll.questions.for_render.sort_for_list - @poll_questions_answers = Poll::Question::Answer.where(question: @poll.questions) - .with_content.order(:given_order) @commentable = @poll @comment_tree = CommentTree.new(@commentable, params[:page], @current_order) end diff --git a/app/views/polls/show.html.erb b/app/views/polls/show.html.erb index 906595616..b911e3391 100644 --- a/app/views/polls/show.html.erb +++ b/app/views/polls/show.html.erb @@ -46,7 +46,7 @@
- <%= render Polls::Questions::ReadMoreAnswerComponent.with_collection(@poll_questions_answers) %> + <%= render Polls::Questions::ReadMoreAnswerComponent.with_collection(@questions) %>
diff --git a/spec/components/polls/questions/read_more_answer_component_spec.rb b/spec/components/polls/questions/read_more_answer_component_spec.rb index 4e4902a81..6c441e878 100644 --- a/spec/components/polls/questions/read_more_answer_component_spec.rb +++ b/spec/components/polls/questions/read_more_answer_component_spec.rb @@ -3,13 +3,28 @@ require "rails_helper" describe Polls::Questions::ReadMoreAnswerComponent do include Rails.application.routes.url_helpers let(:poll) { create(:poll) } - let(:question) { create(:poll_question, poll: poll) } + let(:question) { create(:poll_question, poll: poll, title: "Question title?") } let(:answer) { create(:poll_question_answer, question: question) } + it "renders question title" do + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + + expect(page).to have_content "Question title?" + end + + it "renders answers in the given order" do + create(:poll_question_answer, title: "Answer A", question: question, given_order: 2) + create(:poll_question_answer, title: "Answer B", question: question, given_order: 1) + + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + + expect("Answer B").to appear_before("Answer A") + end + it "renders answers with videos" do create(:poll_answer_video, answer: answer, title: "Awesome video", url: "youtube.com/watch?v=123") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(answer: answer) + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) expect(page).to have_link("Awesome video", href: "youtube.com/watch?v=123") end @@ -17,7 +32,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "renders answers with images" do create(:image, imageable: answer, title: "The yes movement") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(answer: answer) + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) expect(page).to have_css "img[alt='The yes movement']" end @@ -25,7 +40,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "renders answers with documents" do create(:document, documentable: answer, title: "The yes movement") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(answer: answer) + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) expect(page).to have_link("The yes movement") end diff --git a/spec/system/polls/polls_spec.rb b/spec/system/polls/polls_spec.rb index 78cfae471..743b342b1 100644 --- a/spec/system/polls/polls_spec.rb +++ b/spec/system/polls/polls_spec.rb @@ -183,18 +183,6 @@ describe "Polls" do expect("Second question").to appear_before("Third question") end - scenario "More info answers appear in the given order" do - question = create(:poll_question, poll: poll) - answer1 = create(:poll_question_answer, title: "First", question: question, given_order: 2) - answer2 = create(:poll_question_answer, title: "Second", question: question, given_order: 1) - - visit poll_path(poll) - - within("div.poll-more-info-answers") do - expect(answer2.title).to appear_before(answer1.title) - end - end - scenario "Buttons to slide through images work back and forth" do question = create(:poll_question, :yes_no, poll: poll) create(:image, imageable: question.question_answers.last, title: "The no movement") From d70a0bf1d191c9ccc455c478785c56be26986679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= <15726+Senen@users.noreply.github.com> Date: Thu, 22 Sep 2022 13:40:35 +0200 Subject: [PATCH 05/17] Remove instance variable as it is not used --- app/controllers/polls_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index 0828fc9fb..ecb8ffadb 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -19,8 +19,7 @@ class PollsController < ApplicationController def show @questions = @poll.questions.for_render.sort_for_list - @commentable = @poll - @comment_tree = CommentTree.new(@commentable, params[:page], @current_order) + @comment_tree = CommentTree.new(@poll, params[:page], @current_order) end def stats From 4fba76f64f1eafd366502e0a3d8c0582405affaa Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 1 Sep 2022 12:29:26 +0200 Subject: [PATCH 06/17] Do not show answers without additional information --- .../read_more_answer_component.html.erb | 2 +- .../questions/read_more_answer_component.rb | 4 +++ app/models/poll/question.rb | 8 ++++++ app/models/poll/question/answer.rb | 4 +++ .../read_more_answer_component_spec.rb | 10 +++++++ spec/models/poll/question/answer_spec.rb | 26 +++++++++++++++++++ 6 files changed, 53 insertions(+), 1 deletion(-) diff --git a/app/components/polls/questions/read_more_answer_component.html.erb b/app/components/polls/questions/read_more_answer_component.html.erb index 06b374a6d..1810f981a 100644 --- a/app/components/polls/questions/read_more_answer_component.html.erb +++ b/app/components/polls/questions/read_more_answer_component.html.erb @@ -1,5 +1,5 @@

<%= question.title %>

-<% question.question_answers.each do |answer| %> +<% question.answers_with_read_more.each do |answer| %>
" id="answer_<%= answer.id %>">

<%= answer.title %>

diff --git a/app/components/polls/questions/read_more_answer_component.rb b/app/components/polls/questions/read_more_answer_component.rb index a88801581..81d868ab4 100644 --- a/app/components/polls/questions/read_more_answer_component.rb +++ b/app/components/polls/questions/read_more_answer_component.rb @@ -6,4 +6,8 @@ class Polls::Questions::ReadMoreAnswerComponent < ApplicationComponent def initialize(question:) @question = question end + + def render? + question.answers_with_read_more? + end end diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 6be7aecc9..e513d3298 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -74,4 +74,12 @@ class Poll::Question < ApplicationRecord def possible_answers question_answers.joins(:translations).pluck("poll_question_answer_translations.title") end + + def answers_with_read_more? + answers_with_read_more.any? + end + + def answers_with_read_more + question_answers.select(&:with_read_more?) + end end diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index ea251c180..57aba5609 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -40,4 +40,8 @@ class Poll::Question::Answer < ApplicationRecord def total_votes_percentage question.answers_total_votes.zero? ? 0 : (total_votes * 100.0) / question.answers_total_votes end + + def with_read_more? + description.present? || images.any? || documents.any? || videos.any? + end end diff --git a/spec/components/polls/questions/read_more_answer_component_spec.rb b/spec/components/polls/questions/read_more_answer_component_spec.rb index 6c441e878..dff77db77 100644 --- a/spec/components/polls/questions/read_more_answer_component_spec.rb +++ b/spec/components/polls/questions/read_more_answer_component_spec.rb @@ -7,6 +7,8 @@ describe Polls::Questions::ReadMoreAnswerComponent do let(:answer) { create(:poll_question_answer, question: question) } it "renders question title" do + create(:poll_question_answer, question: question, description: "Question answer description") + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) expect(page).to have_content "Question title?" @@ -21,6 +23,14 @@ describe Polls::Questions::ReadMoreAnswerComponent do expect("Answer B").to appear_before("Answer A") end + it "does not render when answers does not have more information" do + answer.update!(description: nil) + + render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + + expect(page).not_to be_rendered + end + it "renders answers with videos" do create(:poll_answer_video, answer: answer, title: "Awesome video", url: "youtube.com/watch?v=123") diff --git a/spec/models/poll/question/answer_spec.rb b/spec/models/poll/question/answer_spec.rb index 2d5b64482..3ee4ddb1e 100644 --- a/spec/models/poll/question/answer_spec.rb +++ b/spec/models/poll/question/answer_spec.rb @@ -34,4 +34,30 @@ describe Poll::Question::Answer do expect(Poll::Question::Answer.with_content).to be_empty end end + + describe "#with_read_more?" do + it "returns false when the answer does not have description, images, videos nor documents" do + poll_question_answer = build(:poll_question_answer, description: nil) + + expect(poll_question_answer.with_read_more?).to be_falsy + end + + it "returns true when the answer has description, images, videos or documents" do + poll_question_answer = build(:poll_question_answer, description: "Answer description") + + expect(poll_question_answer.with_read_more?).to be_truthy + + poll_question_answer = build(:poll_question_answer, :with_image) + + expect(poll_question_answer.with_read_more?).to be_truthy + + poll_question_answer = build(:poll_question_answer, :with_document) + + expect(poll_question_answer.with_read_more?).to be_truthy + + poll_question_answer = build(:poll_question_answer, :with_video) + + expect(poll_question_answer.with_read_more?).to be_truthy + end + end end From e44fe7634a3a51c53d5edd223cba8fe4bef41c99 Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 1 Sep 2022 12:48:53 +0200 Subject: [PATCH 07/17] Change the rendering order of answer additional information --- .../read_more_answer_component.html.erb | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/app/components/polls/questions/read_more_answer_component.html.erb b/app/components/polls/questions/read_more_answer_component.html.erb index 1810f981a..27f87d7ed 100644 --- a/app/components/polls/questions/read_more_answer_component.html.erb +++ b/app/components/polls/questions/read_more_answer_component.html.erb @@ -3,12 +3,8 @@
" id="answer_<%= answer.id %>">

<%= answer.title %>

- <% if answer.images.any? %> - <%= render "polls/gallery", answer: answer %> - <% end %> - - <% if answer.description.present? %> -
+
+ <% if answer.description.present? %>
<%= wysiwyg(answer.description) %>
@@ -25,39 +21,43 @@ <%= t("polls.show.read_less", answer: answer.title) %>
-
- <% end %> + <% end %> - <% if answer.documents.present? %> - - <% end %> + <% if answer.documents.present? %> + - <% end %> + <% if answer.videos.present? %> + + <% end %> +
<% end %> From 4eb4851aba56dc3e9642221a6c5101df75a892b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= <15726+Senen@users.noreply.github.com> Date: Thu, 1 Sep 2022 12:52:23 +0200 Subject: [PATCH 08/17] Rename component as now it renders all question answers additional information --- ...onent.html.erb => read_more_component.html.erb} | 0 ..._answer_component.rb => read_more_component.rb} | 2 +- app/views/polls/show.html.erb | 2 +- ...mponent_spec.rb => read_more_component_spec.rb} | 14 +++++++------- 4 files changed, 9 insertions(+), 9 deletions(-) rename app/components/polls/questions/{read_more_answer_component.html.erb => read_more_component.html.erb} (100%) rename app/components/polls/questions/{read_more_answer_component.rb => read_more_component.rb} (75%) rename spec/components/polls/questions/{read_more_answer_component_spec.rb => read_more_component_spec.rb} (72%) diff --git a/app/components/polls/questions/read_more_answer_component.html.erb b/app/components/polls/questions/read_more_component.html.erb similarity index 100% rename from app/components/polls/questions/read_more_answer_component.html.erb rename to app/components/polls/questions/read_more_component.html.erb diff --git a/app/components/polls/questions/read_more_answer_component.rb b/app/components/polls/questions/read_more_component.rb similarity index 75% rename from app/components/polls/questions/read_more_answer_component.rb rename to app/components/polls/questions/read_more_component.rb index 81d868ab4..035cc2888 100644 --- a/app/components/polls/questions/read_more_answer_component.rb +++ b/app/components/polls/questions/read_more_component.rb @@ -1,4 +1,4 @@ -class Polls::Questions::ReadMoreAnswerComponent < ApplicationComponent +class Polls::Questions::ReadMoreComponent < ApplicationComponent with_collection_parameter :question attr_reader :question delegate :wysiwyg, to: :helpers diff --git a/app/views/polls/show.html.erb b/app/views/polls/show.html.erb index b911e3391..22f1fae32 100644 --- a/app/views/polls/show.html.erb +++ b/app/views/polls/show.html.erb @@ -46,7 +46,7 @@
- <%= render Polls::Questions::ReadMoreAnswerComponent.with_collection(@questions) %> + <%= render Polls::Questions::ReadMoreComponent.with_collection(@questions) %>
diff --git a/spec/components/polls/questions/read_more_answer_component_spec.rb b/spec/components/polls/questions/read_more_component_spec.rb similarity index 72% rename from spec/components/polls/questions/read_more_answer_component_spec.rb rename to spec/components/polls/questions/read_more_component_spec.rb index dff77db77..053bc5097 100644 --- a/spec/components/polls/questions/read_more_answer_component_spec.rb +++ b/spec/components/polls/questions/read_more_component_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -describe Polls::Questions::ReadMoreAnswerComponent do +describe Polls::Questions::ReadMoreComponent do include Rails.application.routes.url_helpers let(:poll) { create(:poll) } let(:question) { create(:poll_question, poll: poll, title: "Question title?") } @@ -9,7 +9,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "renders question title" do create(:poll_question_answer, question: question, description: "Question answer description") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + render_inline Polls::Questions::ReadMoreComponent.new(question: question) expect(page).to have_content "Question title?" end @@ -18,7 +18,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do create(:poll_question_answer, title: "Answer A", question: question, given_order: 2) create(:poll_question_answer, title: "Answer B", question: question, given_order: 1) - render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + render_inline Polls::Questions::ReadMoreComponent.new(question: question) expect("Answer B").to appear_before("Answer A") end @@ -26,7 +26,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "does not render when answers does not have more information" do answer.update!(description: nil) - render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + render_inline Polls::Questions::ReadMoreComponent.new(question: question) expect(page).not_to be_rendered end @@ -34,7 +34,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "renders answers with videos" do create(:poll_answer_video, answer: answer, title: "Awesome video", url: "youtube.com/watch?v=123") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + render_inline Polls::Questions::ReadMoreComponent.new(question: question) expect(page).to have_link("Awesome video", href: "youtube.com/watch?v=123") end @@ -42,7 +42,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "renders answers with images" do create(:image, imageable: answer, title: "The yes movement") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + render_inline Polls::Questions::ReadMoreComponent.new(question: question) expect(page).to have_css "img[alt='The yes movement']" end @@ -50,7 +50,7 @@ describe Polls::Questions::ReadMoreAnswerComponent do it "renders answers with documents" do create(:document, documentable: answer, title: "The yes movement") - render_inline Polls::Questions::ReadMoreAnswerComponent.new(question: question) + render_inline Polls::Questions::ReadMoreComponent.new(question: question) expect(page).to have_link("The yes movement") end From a25629951c840cd8b21cc1d0ff24c049d7d9353c Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 1 Sep 2022 18:20:26 +0200 Subject: [PATCH 09/17] Update poll factories --- spec/factories/polls.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/factories/polls.rb b/spec/factories/polls.rb index 212e7998b..ed493c21c 100644 --- a/spec/factories/polls.rb +++ b/spec/factories/polls.rb @@ -58,6 +58,28 @@ FactoryBot.define do create(:poll_question_answer, question: question, title: "No") end end + + trait :abc do + after(:create) do |question, evaluator| + %w[A B C].each do |letter| + create(:poll_question_answer, question: question, title: "Answer #{letter}") + end + end + end + + factory :poll_question_unique do + after(:create) do |question| + create(:votation_type_unique, questionable: question) + end + end + + factory :poll_question_multiple do + transient { max_votes { 3 } } + + after(:create) do |question, evaluator| + create(:votation_type_multiple, questionable: question, max_votes: evaluator.max_votes) + end + end end factory :poll_question_answer, class: "Poll::Question::Answer" do From 48d7ec75d0f4e085e51555acf67ab3ddfdf0d156 Mon Sep 17 00:00:00 2001 From: decabeza Date: Mon, 5 Sep 2022 16:19:19 +0200 Subject: [PATCH 10/17] Do not allow to create more answers than the maximum defined In case we receive consecutive requests we are locking the poll author record until the first request transaction ends, so the author answers count during subsequent requests validations is up to date. --- app/models/poll/answer.rb | 13 +++++++++++++ spec/models/poll/answer_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb index 685830201..904216355 100644 --- a/app/models/poll/answer.rb +++ b/app/models/poll/answer.rb @@ -7,6 +7,7 @@ class Poll::Answer < ApplicationRecord validates :question, presence: true validates :author, presence: true validates :answer, presence: true + validate :max_votes validates :answer, inclusion: { in: ->(a) { a.question.possible_answers }}, unless: ->(a) { a.question.blank? } @@ -21,4 +22,16 @@ class Poll::Answer < ApplicationRecord Poll::Voter.find_or_create_by!(user: author, poll: poll, origin: "web") end end + + private + + def max_votes + return if !question || question&.unique? || persisted? + + author.lock! + + if question.answers.by_author(author).count >= question.max_votes + errors.add(:answer, "Maximum number of votes per user exceeded") + end + end end diff --git a/spec/models/poll/answer_spec.rb b/spec/models/poll/answer_spec.rb index d420e367e..b2b419c23 100644 --- a/spec/models/poll/answer_spec.rb +++ b/spec/models/poll/answer_spec.rb @@ -23,6 +23,30 @@ describe Poll::Answer do expect(answer).not_to be_valid end + it "is not valid when user already reached multiple answers question max votes" do + author = create(:user) + question = create(:poll_question_multiple, :abc, max_votes: 2) + create(:poll_answer, author: author, question: question, answer: "Answer A") + create(:poll_answer, author: author, question: question, answer: "Answer B") + answer = build(:poll_answer, author: author, question: question, answer: "Answer C") + + expect(answer).not_to be_valid + end + + it "validates max votes when creating answers at the same time", :race_condition do + author = create(:user, :level_two) + question = create(:poll_question_multiple, :abc, max_votes: 2) + create(:poll_answer, question: question, answer: "Answer A", author: author) + answer = build(:poll_answer, question: question, answer: "Answer B", author: author) + other_answer = build(:poll_answer, question: question, answer: "Answer C", author: author) + + [answer, other_answer].map do |a| + Thread.new { a.save } + end.each(&:join) + + expect(Poll::Answer.count).to be 2 + end + it "is valid for answers included in the Poll::Question's question_answers list" do question = create(:poll_question) create(:poll_question_answer, title: "One", question: question) From f90d0d9c4dedc89f154c79679e1c78661f9014b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= <15726+Senen@users.noreply.github.com> Date: Thu, 29 Sep 2022 12:20:35 +0200 Subject: [PATCH 11/17] Show poll user answers in subsequent sessions --- .../questions/answers_component.html.erb | 2 +- .../polls/questions/answers_component.rb | 6 --- .../polls/questions/answers_component_spec.rb | 13 ------- spec/system/polls/polls_spec.rb | 38 ------------------- spec/system/polls/voter_spec.rb | 25 ------------ 5 files changed, 1 insertion(+), 83 deletions(-) diff --git a/app/components/polls/questions/answers_component.html.erb b/app/components/polls/questions/answers_component.html.erb index 91be9ab15..053a514ad 100644 --- a/app/components/polls/questions/answers_component.html.erb +++ b/app/components/polls/questions/answers_component.html.erb @@ -1,7 +1,7 @@
<% if can?(:answer, question) && !question.poll.voted_in_booth?(current_user) %> <% question_answers.each do |question_answer| %> - <% if already_answered?(question_answer) && !voted_before_sign_in? %> + <% if already_answered?(question_answer) %> "> <%= question_answer.title %> diff --git a/app/components/polls/questions/answers_component.rb b/app/components/polls/questions/answers_component.rb index f51ad4e7f..e007dd1c2 100644 --- a/app/components/polls/questions/answers_component.rb +++ b/app/components/polls/questions/answers_component.rb @@ -10,12 +10,6 @@ class Polls::Questions::AnswersComponent < ApplicationComponent user_answers.find_by(answer: question_answer.title).present? end - def voted_before_sign_in? - user_answers.any? do |vote| - vote.updated_at < current_user.current_sign_in_at - end - end - def question_answers question.question_answers end diff --git a/spec/components/polls/questions/answers_component_spec.rb b/spec/components/polls/questions/answers_component_spec.rb index e0a543a16..6fadded57 100644 --- a/spec/components/polls/questions/answers_component_spec.rb +++ b/spec/components/polls/questions/answers_component_spec.rb @@ -22,7 +22,6 @@ describe Polls::Questions::AnswersComponent do it "renders a span instead of a button for existing user answers" do user = create(:user, :verified) - allow(user).to receive(:current_sign_in_at).and_return(user.created_at) create(:poll_answer, author: user, question: question, answer: "Yes") sign_in(user) @@ -33,18 +32,6 @@ describe Polls::Questions::AnswersComponent do expect(page).to have_button "No" end - it "hides current answer and shows buttons in successive sessions" do - user = create(:user, :verified) - create(:poll_answer, author: user, question: question, answer: "Yes") - allow(user).to receive(:current_sign_in_at).and_return(Time.current) - sign_in(user) - - render_inline Polls::Questions::AnswersComponent.new(question) - - expect(page).to have_button "Yes" - expect(page).to have_button "No" - end - it "when user is not signed in, renders answers links pointing to user sign in path" do render_inline Polls::Questions::AnswersComponent.new(question) diff --git a/spec/system/polls/polls_spec.rb b/spec/system/polls/polls_spec.rb index 743b342b1..59ee5b319 100644 --- a/spec/system/polls/polls_spec.rb +++ b/spec/system/polls/polls_spec.rb @@ -276,44 +276,6 @@ describe "Polls" do end end - scenario "Level 2 votes, signs out, signs in, votes again" do - poll.update!(geozone_restricted: true) - poll.geozones << geozone - - question = create(:poll_question, :yes_no, poll: poll) - user = create(:user, :level_two, geozone: geozone) - - login_as user - visit poll_path(poll) - - within("#poll_question_#{question.id}_answers") do - click_button "Yes" - - expect(page).not_to have_button "Yes" - expect(page).to have_button "No" - end - - click_link "Sign out" - login_as user - visit poll_path(poll) - within("#poll_question_#{question.id}_answers") do - click_button "Yes" - - expect(page).not_to have_button "Yes" - expect(page).to have_button "No" - end - - click_link "Sign out" - login_as user - visit poll_path(poll) - within("#poll_question_#{question.id}_answers") do - click_button "No" - - expect(page).not_to have_button "No" - expect(page).to have_button "Yes" - end - end - scenario "Shows SDG tags when feature is enabled" do Setting["feature.sdg"] = true Setting["sdg.process.polls"] = true diff --git a/spec/system/polls/voter_spec.rb b/spec/system/polls/voter_spec.rb index a629dcdf6..5d8daef34 100644 --- a/spec/system/polls/voter_spec.rb +++ b/spec/system/polls/voter_spec.rb @@ -163,31 +163,6 @@ describe "Voter" do expect(page).to have_content "1" end end - - scenario "Trying to vote in web again" do - login_as user - vote_for_poll_via_web(poll, question, answer_yes.title) - expect(Poll::Voter.count).to eq(1) - - visit poll_path(poll) - - expect(page).to have_content "You have already participated in this poll. If you vote again it will be overwritten." - within("#poll_question_#{question.id}_answers") do - expect(page).not_to have_button(answer_yes.title) - end - - unfreeze_time - - click_link "Sign out" - - login_as user - visit poll_path(poll) - - within("#poll_question_#{question.id}_answers") do - expect(page).to have_button(answer_yes.title) - expect(page).to have_button(answer_no.title) - end - end end scenario "Voting in poll and then verifiying account" do From 7df0e9a961d83b81c8458370b9169540d2baefac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= <15726+Senen@users.noreply.github.com> Date: Fri, 30 Sep 2022 18:09:24 +0200 Subject: [PATCH 12/17] Allow to remove poll answers --- .../questions/answers_component.html.erb | 13 +++++++++---- .../polls/questions/answers_component.rb | 6 +++++- app/controllers/polls/answers_controller.rb | 19 +++++++++++++++++++ app/models/abilities/common.rb | 3 +++ config/routes/poll.rb | 1 + .../polls/questions/answers_component_spec.rb | 9 +++++---- spec/models/abilities/common_spec.rb | 12 ++++++++++++ spec/support/common_actions/polls.rb | 4 ++-- spec/system/polls/polls_spec.rb | 16 ++++++++-------- spec/system/polls/voter_spec.rb | 6 ++++-- 10 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 app/controllers/polls/answers_controller.rb diff --git a/app/components/polls/questions/answers_component.html.erb b/app/components/polls/questions/answers_component.html.erb index 053a514ad..de84bcd7f 100644 --- a/app/components/polls/questions/answers_component.html.erb +++ b/app/components/polls/questions/answers_component.html.erb @@ -2,15 +2,20 @@ <% if can?(:answer, question) && !question.poll.voted_in_booth?(current_user) %> <% question_answers.each do |question_answer| %> <% if already_answered?(question_answer) %> - "> + <%= button_to question_answer_path(question, user_answer(question_answer)), + method: :delete, + remote: true, + title: t("poll_questions.show.voted", answer: question_answer.title), + class: "button answered", + "aria-pressed": true do %> <%= question_answer.title %> - + <% end %> <% else %> <%= button_to answer_question_path(question, answer: question_answer.title), remote: true, title: t("poll_questions.show.vote_answer", answer: question_answer.title), - class: "button secondary hollow" do %> + class: "button secondary hollow", + "aria-pressed": false do %> <%= question_answer.title %> <% end %> <% end %> diff --git a/app/components/polls/questions/answers_component.rb b/app/components/polls/questions/answers_component.rb index e007dd1c2..b21459a97 100644 --- a/app/components/polls/questions/answers_component.rb +++ b/app/components/polls/questions/answers_component.rb @@ -7,13 +7,17 @@ class Polls::Questions::AnswersComponent < ApplicationComponent end def already_answered?(question_answer) - user_answers.find_by(answer: question_answer.title).present? + user_answer(question_answer).present? end def question_answers question.question_answers end + def user_answer(question_answer) + user_answers.find_by(answer: question_answer.title) + end + private def user_answers diff --git a/app/controllers/polls/answers_controller.rb b/app/controllers/polls/answers_controller.rb new file mode 100644 index 000000000..46a40185d --- /dev/null +++ b/app/controllers/polls/answers_controller.rb @@ -0,0 +1,19 @@ +class Polls::AnswersController < ApplicationController + load_and_authorize_resource :question, class: "::Poll::Question" + load_and_authorize_resource :answer, class: "::Poll::Answer", + through: :question, + through_association: :answers + + def destroy + @answer.destroy! + + respond_to do |format| + format.html do + redirect_to request.referer + end + format.js do + render "polls/questions/answers" + end + end + end +end diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb index 2da9a057d..6c8d6cdd2 100644 --- a/app/models/abilities/common.rb +++ b/app/models/abilities/common.rb @@ -112,6 +112,9 @@ module Abilities can :answer, Poll::Question do |question| question.answerable_by?(user) end + can :destroy, Poll::Answer do |answer| + answer.author == user && answer.question.answerable_by?(user) + end end can [:create, :show], ProposalNotification, proposal: { author_id: user.id } diff --git a/config/routes/poll.rb b/config/routes/poll.rb index 5b3c03c7a..94cf504b4 100644 --- a/config/routes/poll.rb +++ b/config/routes/poll.rb @@ -6,6 +6,7 @@ resources :polls, only: [:show, :index] do resources :questions, controller: "polls/questions", shallow: true do post :answer, on: :member + resources :answers, controller: "polls/answers", only: :destroy, shallow: false end end diff --git a/spec/components/polls/questions/answers_component_spec.rb b/spec/components/polls/questions/answers_component_spec.rb index 6fadded57..0b05953c2 100644 --- a/spec/components/polls/questions/answers_component_spec.rb +++ b/spec/components/polls/questions/answers_component_spec.rb @@ -18,18 +18,19 @@ describe Polls::Questions::AnswersComponent do expect(page).to have_button "Yes" expect(page).to have_button "No" + expect(page).to have_css "button[aria-pressed='false']", count: 2 end - it "renders a span instead of a button for existing user answers" do + it "renders button to destroy current user answers" do user = create(:user, :verified) create(:poll_answer, author: user, question: question, answer: "Yes") sign_in(user) render_inline Polls::Questions::AnswersComponent.new(question) - expect(page).to have_selector "span", text: "Yes" - expect(page).not_to have_button "Yes" - expect(page).to have_button "No" + expect(page).to have_button "You have voted Yes" + expect(page).to have_button "Vote No" + expect(page).to have_css "button[aria-pressed='true']", text: "Yes" end it "when user is not signed in, renders answers links pointing to user sign in path" do diff --git a/spec/models/abilities/common_spec.rb b/spec/models/abilities/common_spec.rb index 42194209a..81eb67e38 100644 --- a/spec/models/abilities/common_spec.rb +++ b/spec/models/abilities/common_spec.rb @@ -232,6 +232,18 @@ describe Abilities::Common do it { should_not be_able_to(:answer, expired_poll_question_from_all_geozones) } it { should_not be_able_to(:answer, expired_poll_question_from_other_geozone) } + context "Poll::Answer" do + let(:own_answer) { create(:poll_answer, author: user) } + let(:other_user_answer) { create(:poll_answer) } + let(:expired_poll) { create(:poll, :expired) } + let(:question) { create(:poll_question, :yes_no, poll: expired_poll) } + let(:expired_poll_answer) { create(:poll_answer, author: user, question: question, answer: "Yes") } + + it { should be_able_to(:destroy, own_answer) } + it { should_not be_able_to(:destroy, other_user_answer) } + it { should_not be_able_to(:destroy, expired_poll_answer) } + end + context "without geozone" do before { user.geozone = nil } diff --git a/spec/support/common_actions/polls.rb b/spec/support/common_actions/polls.rb index b571472c4..68cc1c6c0 100644 --- a/spec/support/common_actions/polls.rb +++ b/spec/support/common_actions/polls.rb @@ -5,8 +5,8 @@ module Polls within("#poll_question_#{question.id}_answers") do click_button answer - expect(page).to have_css("span.answered", text: answer) - expect(page).not_to have_button(answer) + expect(page).to have_button("You have voted #{answer}") + expect(page).not_to have_button("Vote #{answer}") end end diff --git a/spec/system/polls/polls_spec.rb b/spec/system/polls/polls_spec.rb index 59ee5b319..6d15c2d78 100644 --- a/spec/system/polls/polls_spec.rb +++ b/spec/system/polls/polls_spec.rb @@ -246,10 +246,10 @@ describe "Polls" do visit poll_path(poll) within("#poll_question_#{question.id}_answers") do - click_button "Yes" + click_button "Vote Yes" - expect(page).not_to have_button "Yes" - expect(page).to have_button "No" + expect(page).to have_button "You have voted Yes" + expect(page).to have_button "Vote No" end end @@ -266,13 +266,13 @@ describe "Polls" do within("#poll_question_#{question.id}_answers") do click_button "Yes" - expect(page).not_to have_button "Yes" - expect(page).to have_button "No" + expect(page).to have_button "You have voted Yes" + expect(page).to have_button "Vote No" click_button "No" - expect(page).not_to have_button "No" - expect(page).to have_button "Yes" + expect(page).to have_button "Vote Yes" + expect(page).to have_button "You have voted No" end end @@ -309,7 +309,7 @@ describe "Polls" do within("#poll_question_#{question.id}_answers") do click_button "Yes" - expect(page).not_to have_button "Yes" + expect(page).to have_button "You have voted Yes" expect(page).to have_button "No" end end diff --git a/spec/system/polls/voter_spec.rb b/spec/system/polls/voter_spec.rb index 5d8daef34..30cc577fd 100644 --- a/spec/system/polls/voter_spec.rb +++ b/spec/system/polls/voter_spec.rb @@ -23,8 +23,10 @@ describe "Voter" do visit poll_path(poll) within("#poll_question_#{question.id}_answers") do - click_button answer_yes.title - expect(page).not_to have_button(answer_yes.title) + click_button "Vote Yes" + + expect(page).to have_button("You have voted Yes") + expect(page).not_to have_button("Vote Yes") end expect(Poll::Voter.count).to eq(1) From 3da4112d94c62caf18d2cec5bc9e8a10ce92a12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sen=C3=A9n=20Rodero=20Rodr=C3=ADguez?= <15726+Senen@users.noreply.github.com> Date: Sun, 11 Sep 2022 18:57:05 +0200 Subject: [PATCH 13/17] Remove `Poll::Voter` record when there is no more user answers Now we can remove answers we should provide a way of removing voting. --- app/controllers/polls/answers_controller.rb | 2 +- app/models/poll/answer.rb | 10 +++++ spec/models/poll/answer_spec.rb | 28 +++++++++++++ spec/system/polls/voter_spec.rb | 44 ++++++++++++++++----- 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/app/controllers/polls/answers_controller.rb b/app/controllers/polls/answers_controller.rb index 46a40185d..9e4988c05 100644 --- a/app/controllers/polls/answers_controller.rb +++ b/app/controllers/polls/answers_controller.rb @@ -5,7 +5,7 @@ class Polls::AnswersController < ApplicationController through_association: :answers def destroy - @answer.destroy! + @answer.destroy_and_remove_voter_participation respond_to do |format| format.html do diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb index 904216355..41d76e925 100644 --- a/app/models/poll/answer.rb +++ b/app/models/poll/answer.rb @@ -23,6 +23,16 @@ class Poll::Answer < ApplicationRecord end end + def destroy_and_remove_voter_participation + transaction do + destroy! + + if author.poll_answers.where(question_id: poll.question_ids).none? + Poll::Voter.find_by(user: author, poll: poll, origin: "web").destroy! + end + end + end + private def max_votes diff --git a/spec/models/poll/answer_spec.rb b/spec/models/poll/answer_spec.rb index b2b419c23..691a09943 100644 --- a/spec/models/poll/answer_spec.rb +++ b/spec/models/poll/answer_spec.rb @@ -106,4 +106,32 @@ describe Poll::Answer do expect(answer).not_to be_persisted end end + + describe "#destroy_and_remove_voter_participation" do + let(:poll) { create(:poll) } + let(:question) { create(:poll_question, :yes_no, poll: poll) } + + it "destroys voter record and answer when it was the only user's answer" do + answer = build(:poll_answer, question: question) + answer.save_and_record_voter_participation + + expect { answer.destroy_and_remove_voter_participation } + .to change { Poll::Answer.count }.by(-1) + .and change { Poll::Voter.count }.by(-1) + end + + it "destroys the answer but does not destroy the voter record when the user + has answered other poll questions" do + answer = build(:poll_answer, question: question) + answer.save_and_record_voter_participation + other_question = create(:poll_question, :yes_no, poll: poll) + other_answer = build(:poll_answer, question: other_question, author: answer.author) + other_answer.save_and_record_voter_participation + + expect(other_answer).to be_persisted + expect { answer.destroy_and_remove_voter_participation } + .to change { Poll::Answer.count }.by(-1) + .and change { Poll::Voter.count }.by(0) + end + end end diff --git a/spec/system/polls/voter_spec.rb b/spec/system/polls/voter_spec.rb index 30cc577fd..46086b2e5 100644 --- a/spec/system/polls/voter_spec.rb +++ b/spec/system/polls/voter_spec.rb @@ -3,12 +3,10 @@ require "rails_helper" describe "Voter" do context "Origin", :with_frozen_time do let(:poll) { create(:poll) } - let(:question) { create(:poll_question, poll: poll) } + let!(:question) { create(:poll_question, :yes_no, poll: poll) } let(:booth) { create(:poll_booth) } let(:officer) { create(:poll_officer) } let(:admin) { create(:administrator) } - let!(:answer_yes) { create(:poll_question_answer, question: question, title: "Yes") } - let!(:answer_no) { create(:poll_question_answer, question: question, title: "No") } before do create(:geozone, :in_census) @@ -29,8 +27,34 @@ describe "Voter" do expect(page).not_to have_button("Vote Yes") end - expect(Poll::Voter.count).to eq(1) - expect(Poll::Voter.first.origin).to eq("web") + visit poll_path(poll) + + expect(page).to have_content("You have already participated in this poll.") + expect(page).to have_content("If you vote again it will be overwritten") + end + + scenario "Remove vote via web - Standard" do + user = create(:user, :level_two) + create(:poll_answer, question: question, author: user, answer: "Yes") + create(:poll_voter, poll: poll, user: user) + + login_as user + visit poll_path(poll) + + expect(page).to have_content("You have already participated in this poll.") + expect(page).to have_content("If you vote again it will be overwritten") + + within("#poll_question_#{question.id}_answers") do + click_button "You have voted Yes" + + expect(page).to have_button("Vote Yes") + expect(page).to have_button("Vote No") + end + + visit poll_path(poll) + + expect(page).not_to have_content("You have already participated in this poll.") + expect(page).not_to have_content("If you vote again it will be overwritten") end scenario "Voting via web as unverified user" do @@ -40,8 +64,8 @@ describe "Voter" do visit poll_path(poll) within("#poll_question_#{question.id}_answers") do - expect(page).to have_link(answer_yes.title, href: verification_path) - expect(page).to have_link(answer_no.title, href: verification_path) + expect(page).to have_link("Yes", href: verification_path) + expect(page).to have_link("No", href: verification_path) end expect(page).to have_content("You must verify your account in order to answer") @@ -120,7 +144,7 @@ describe "Voter" do scenario "Trying to vote in web and then in booth" do login_as user - vote_for_poll_via_web(poll, question, answer_yes.title) + vote_for_poll_via_web(poll, question, "Yes") expect(Poll::Voter.count).to eq(1) click_link "Sign out" @@ -147,7 +171,7 @@ describe "Voter" do visit poll_path(poll) within("#poll_question_#{question.id}_answers") do - expect(page).not_to have_button(answer_yes.title) + expect(page).not_to have_button("Yes") end expect(page).to have_content "You have already participated in a physical booth. You can not participate again." expect(Poll::Voter.count).to eq(1) @@ -186,7 +210,7 @@ describe "Voter" do visit poll_path(poll) within("#poll_question_#{question.id}_answers") do - expect(page).not_to have_button(answer_yes.title) + expect(page).not_to have_button("Yes") end expect(page).to have_content "You have already participated in a physical booth. You can not participate again." From 36e452437e3ba6c651a3bd195d006f9dcb6e7b69 Mon Sep 17 00:00:00 2001 From: decabeza Date: Fri, 2 Sep 2022 16:41:17 +0200 Subject: [PATCH 14/17] Add questions with mutiple answers to polls public interface The `reload` method added to max_votes validation is needed because the author gets here with some changes because of the around_action `switch_locale`, which adds some changes to the current user record and therefore, the lock method raises an exception when trying to lock it requiring us to save or discard those record changes. --- .../questions/answers_component.html.erb | 3 +- .../polls/questions/answers_component.rb | 4 ++ .../questions/question_component.html.erb | 6 ++ app/controllers/polls/questions_controller.rb | 6 +- app/models/concerns/questionable.rb | 17 +++++ app/models/poll/answer.rb | 1 + .../{answer.js.erb => answers.js.erb} | 0 config/locales/en/general.yml | 3 + config/locales/es/general.yml | 3 + .../polls/questions/answers_component_spec.rb | 14 ++++ spec/system/polls/votation_types_spec.rb | 69 +++++++++++++++++++ 11 files changed, 121 insertions(+), 5 deletions(-) rename app/views/polls/questions/{answer.js.erb => answers.js.erb} (100%) create mode 100644 spec/system/polls/votation_types_spec.rb diff --git a/app/components/polls/questions/answers_component.html.erb b/app/components/polls/questions/answers_component.html.erb index de84bcd7f..c73247ec2 100644 --- a/app/components/polls/questions/answers_component.html.erb +++ b/app/components/polls/questions/answers_component.html.erb @@ -15,7 +15,8 @@ remote: true, title: t("poll_questions.show.vote_answer", answer: question_answer.title), class: "button secondary hollow", - "aria-pressed": false do %> + "aria-pressed": false, + disabled: disable_answer?(question_answer) do %> <%= question_answer.title %> <% end %> <% end %> diff --git a/app/components/polls/questions/answers_component.rb b/app/components/polls/questions/answers_component.rb index b21459a97..0509d8fa7 100644 --- a/app/components/polls/questions/answers_component.rb +++ b/app/components/polls/questions/answers_component.rb @@ -18,6 +18,10 @@ class Polls::Questions::AnswersComponent < ApplicationComponent user_answers.find_by(answer: question_answer.title) end + def disable_answer?(question_answer) + question.multiple? && user_answers.count == question.max_votes + end + private def user_answers diff --git a/app/components/polls/questions/question_component.html.erb b/app/components/polls/questions/question_component.html.erb index 5fd7eeb8b..3cd99a1b4 100644 --- a/app/components/polls/questions/question_component.html.erb +++ b/app/components/polls/questions/question_component.html.erb @@ -3,6 +3,12 @@ <%= question.title %> + <% if question.votation_type.present? %> + + <%= t("poll_questions.description.#{question.vote_type}", maximum: question.max_votes) %> + + <% end %> +
<%= render Polls::Questions::AnswersComponent.new(question) %>
diff --git a/app/controllers/polls/questions_controller.rb b/app/controllers/polls/questions_controller.rb index 3731aa2f9..a21acc9bc 100644 --- a/app/controllers/polls/questions_controller.rb +++ b/app/controllers/polls/questions_controller.rb @@ -5,9 +5,7 @@ class Polls::QuestionsController < ApplicationController has_orders %w[most_voted newest oldest], only: :show def answer - answer = @question.answers.find_or_initialize_by(author: current_user) - - answer.answer = params[:answer] + answer = @question.find_or_initialize_user_answer(current_user, params[:answer]) answer.save_and_record_voter_participation respond_to do |format| @@ -15,7 +13,7 @@ class Polls::QuestionsController < ApplicationController redirect_to request.referer end format.js do - render :answer + render :answers end end end diff --git a/app/models/concerns/questionable.rb b/app/models/concerns/questionable.rb index f7b20b8f4..1d0606fc6 100644 --- a/app/models/concerns/questionable.rb +++ b/app/models/concerns/questionable.rb @@ -10,4 +10,21 @@ module Questionable def unique? votation_type.nil? || votation_type.unique? end + + def find_or_initialize_user_answer(user, title) + answer = answers.find_or_initialize_by(find_by_attributes(user, title)) + answer.answer = title + answer + end + + private + + def find_by_attributes(user, title) + case vote_type + when "unique", nil + { author: user } + when "multiple" + { author: user, answer: title } + end + end end diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb index 41d76e925..d9d86e9d6 100644 --- a/app/models/poll/answer.rb +++ b/app/models/poll/answer.rb @@ -38,6 +38,7 @@ class Poll::Answer < ApplicationRecord def max_votes return if !question || question&.unique? || persisted? + author.reload author.lock! if question.answers.by_author(author).count >= question.max_votes diff --git a/app/views/polls/questions/answer.js.erb b/app/views/polls/questions/answers.js.erb similarity index 100% rename from app/views/polls/questions/answer.js.erb rename to app/views/polls/questions/answers.js.erb diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 4a70c1a3e..afc277ef4 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -636,6 +636,9 @@ en: show: vote_answer: "Vote %{answer}" voted: "You have voted %{answer}" + description: + unique: "You can select a maximum of 1 answer." + multiple: "You can select a maximum of %{maximum} answers." proposal_notifications: new: title: "Send notification" diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index d731f4bd4..382b302e6 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -636,6 +636,9 @@ es: show: vote_answer: "Votar %{answer}" voted: "Has votado %{answer}" + description: + unique: "Puedes seleccionar un máximo de 1 respuesta." + multiple: "Puedes seleccionar un máximo de %{maximum} respuestas." proposal_notifications: new: title: "Enviar notificación" diff --git a/spec/components/polls/questions/answers_component_spec.rb b/spec/components/polls/questions/answers_component_spec.rb index 0b05953c2..a5460b83e 100644 --- a/spec/components/polls/questions/answers_component_spec.rb +++ b/spec/components/polls/questions/answers_component_spec.rb @@ -33,6 +33,20 @@ describe Polls::Questions::AnswersComponent do expect(page).to have_css "button[aria-pressed='true']", text: "Yes" end + it "renders disabled buttons when max votes is reached" do + user = create(:user, :verified) + question = create(:poll_question_multiple, :abc, max_votes: 2, author: user) + create(:poll_answer, author: user, question: question, answer: "Answer A") + create(:poll_answer, author: user, question: question, answer: "Answer C") + sign_in(user) + + render_inline Polls::Questions::AnswersComponent.new(question) + + expect(page).to have_button "You have voted Answer A" + expect(page).to have_button "Vote Answer B", disabled: true + expect(page).to have_button "You have voted Answer C" + end + it "when user is not signed in, renders answers links pointing to user sign in path" do render_inline Polls::Questions::AnswersComponent.new(question) diff --git a/spec/system/polls/votation_types_spec.rb b/spec/system/polls/votation_types_spec.rb new file mode 100644 index 000000000..51efb7f2c --- /dev/null +++ b/spec/system/polls/votation_types_spec.rb @@ -0,0 +1,69 @@ +require "rails_helper" + +describe "Poll Votation Type" do + let(:author) { create(:user, :level_two) } + + before do + login_as(author) + end + + scenario "Unique answer" do + question = create(:poll_question_unique, :yes_no) + + visit poll_path(question.poll) + + expect(page).to have_content "You can select a maximum of 1 answer." + expect(page).to have_content(question.title) + expect(page).to have_button("Vote Yes") + expect(page).to have_button("Vote No") + + within "#poll_question_#{question.id}_answers" do + click_button "Yes" + + expect(page).to have_button("You have voted Yes") + expect(page).to have_button("Vote No") + + click_button "No" + + expect(page).to have_button("Vote Yes") + expect(page).to have_button("You have voted No") + end + end + + scenario "Multiple answers" do + question = create(:poll_question_multiple, :abc, max_votes: 2) + visit poll_path(question.poll) + + expect(page).to have_content "You can select a maximum of 2 answers." + expect(page).to have_content(question.title) + expect(page).to have_button("Vote Answer A") + expect(page).to have_button("Vote Answer B") + expect(page).to have_button("Vote Answer C") + + within "#poll_question_#{question.id}_answers" do + click_button "Vote Answer A" + + expect(page).to have_button("You have voted Answer A") + + click_button "Vote Answer C" + + expect(page).to have_button("You have voted Answer C") + expect(page).to have_button("Vote Answer B", disabled: true) + + click_button "You have voted Answer A" + + expect(page).to have_button("Vote Answer A") + expect(page).to have_button("Vote Answer B") + + click_button "You have voted Answer C" + + expect(page).to have_button("Vote Answer C") + + click_button "Vote Answer B" + + expect(page).to have_button("You have voted Answer B") + expect(page).to have_button("Vote Answer A") + expect(page).to have_button("Vote Answer C") + end + end +end From 105bb0db3171782f51d313b7ba119e69e344425d Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 1 Sep 2022 17:18:27 +0200 Subject: [PATCH 15/17] Add `votation_types` to development seeds --- db/dev_seeds/polls.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/db/dev_seeds/polls.rb b/db/dev_seeds/polls.rb index cf9e97fbe..fcdf9f94b 100644 --- a/db/dev_seeds/polls.rb +++ b/db/dev_seeds/polls.rb @@ -55,7 +55,7 @@ end section "Creating Poll Questions & Answers" do Poll.find_each do |poll| - (1..4).to_a.sample.times do + (3..5).to_a.sample.times do question_title = Faker::Lorem.sentence(word_count: 3).truncate(60) + "?" question = Poll::Question.new(author: User.all.sample, title: question_title, @@ -84,6 +84,15 @@ section "Creating Poll Questions & Answers" do end end +section "Creating Poll Votation types" do + poll = Poll.first + + poll.questions.each do |question| + vote_type = VotationType.vote_types.keys.sample + question.create_votation_type!(vote_type: vote_type, max_votes: (3 unless vote_type == "unique")) + end +end + section "Creating Poll Booths & BoothAssignments" do 20.times do |i| Poll::Booth.create(name: "Booth #{i}", From 88007d0c5ddea95cb6aab4910e1b29319c0ff050 Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 8 Sep 2022 18:18:37 +0200 Subject: [PATCH 16/17] Update poll question answer description translation --- config/locales/en/activerecord.yml | 2 +- config/locales/es/activerecord.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index 7b1311da8..0e9f59e65 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -454,7 +454,7 @@ en: description: Description poll/question/answer/translation: title: Answer - description: Description + description: "Description (optional)" poll/question/answer/video: title: Title url: External video diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index 99ce9c5b0..58305df15 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -454,7 +454,7 @@ es: description: Descripción poll/question/answer/translation: title: Respuesta - description: Descripción + description: "Descripción (opcional)" poll/question/answer/video: title: Título url: Vídeo externo From 1eb52fb0450ca104f300bf2a8a64d039484f250f Mon Sep 17 00:00:00 2001 From: decabeza Date: Fri, 9 Sep 2022 13:20:55 +0200 Subject: [PATCH 17/17] Add read more links to poll question component --- .../questions/question_component.html.erb | 7 +++++++ .../polls/questions/question_component.rb | 6 ++++++ config/locales/en/general.yml | 1 + config/locales/es/general.yml | 1 + .../polls/questions/question_component_spec.rb | 18 ++++++++++++++++++ 5 files changed, 33 insertions(+) create mode 100644 spec/components/polls/questions/question_component_spec.rb diff --git a/app/components/polls/questions/question_component.html.erb b/app/components/polls/questions/question_component.html.erb index 3cd99a1b4..ac675a889 100644 --- a/app/components/polls/questions/question_component.html.erb +++ b/app/components/polls/questions/question_component.html.erb @@ -12,4 +12,11 @@
<%= render Polls::Questions::AnswersComponent.new(question) %>
+ + <% if question.answers_with_read_more? %> +
+

<%= t("poll_questions.read_more_about") %>

+

<%= answers_read_more_links %>

+
+ <% end %>
diff --git a/app/components/polls/questions/question_component.rb b/app/components/polls/questions/question_component.rb index d1a4edf27..09b8cacd9 100644 --- a/app/components/polls/questions/question_component.rb +++ b/app/components/polls/questions/question_component.rb @@ -4,4 +4,10 @@ class Polls::Questions::QuestionComponent < ApplicationComponent def initialize(question:) @question = question end + + def answers_read_more_links + safe_join(question.answers_with_read_more.map do |answer| + link_to answer.title, "#answer_#{answer.id}" + end, ", ") + end end diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index afc277ef4..9d7c80499 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -639,6 +639,7 @@ en: description: unique: "You can select a maximum of 1 answer." multiple: "You can select a maximum of %{maximum} answers." + read_more_about: "Read more about:" proposal_notifications: new: title: "Send notification" diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 382b302e6..c69b4259e 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -639,6 +639,7 @@ es: description: unique: "Puedes seleccionar un máximo de 1 respuesta." multiple: "Puedes seleccionar un máximo de %{maximum} respuestas." + read_more_about: "Leer más:" proposal_notifications: new: title: "Enviar notificación" diff --git a/spec/components/polls/questions/question_component_spec.rb b/spec/components/polls/questions/question_component_spec.rb new file mode 100644 index 000000000..4ca884af5 --- /dev/null +++ b/spec/components/polls/questions/question_component_spec.rb @@ -0,0 +1,18 @@ +require "rails_helper" + +describe Polls::Questions::QuestionComponent do + it "renders more information links when any question answer has additional information" do + question = create(:poll_question) + answer_a = create(:poll_question_answer, question: question, title: "Answer A") + answer_b = create(:poll_question_answer, question: question, title: "Answer B") + allow_any_instance_of(Poll::Question::Answer).to receive(:with_read_more?).and_return(true) + + render_inline Polls::Questions::QuestionComponent.new(question: question) + + poll_question = page.find("#poll_question_#{question.id}") + expect(poll_question).to have_content("Read more about") + expect(poll_question).to have_link("Answer A", href: "#answer_#{answer_a.id}") + expect(poll_question).to have_link("Answer B", href: "#answer_#{answer_b.id}") + expect(poll_question).to have_content("Answer A, Answer B") + end +end