diff --git a/app/components/polls/results/question_component.html.erb b/app/components/polls/results/question_component.html.erb index 512d012f8..422ec44bb 100644 --- a/app/components/polls/results/question_component.html.erb +++ b/app/components/polls/results/question_component.html.erb @@ -1,25 +1,46 @@

<%= question.title %>

- - - <%- question.question_options.each do |option| %> - - <% end %> - - - - - <%- question.question_options.each do |option| %> - + + <%- question.question_options.each do |option| %> + + <% end %> + + + + + <%- question.question_options.each do |option| %> + + <% end %> + + + <% else %> + + + + + + + + + - <% end %> - - + + + + <% end %>
- <% if most_voted_option?(option) %> - <%= t("polls.show.results.most_voted_answer") %> - <% end %> - <%= option.title %> -
- <%= option.total_votes %> - (<%= option.total_votes_percentage.round(2) %>%) + <% if question.accepts_options? %> +
+ <% if most_voted_option?(option) %> + <%= t("polls.show.results.most_voted_answer") %> + <% end %> + <%= option.title %> +
+ <%= option.total_votes %> + (<%= option.total_votes_percentage.round(2) %>%) +
<%= t("polls.show.results.open_ended.valid") %><%= t("polls.show.results.open_ended.blank") %>
+ <%= question.open_ended_valid_answers_count %> + (<%= question.open_ended_valid_percentage.round(2) %>%)
+ <%= question.open_ended_blank_answers_count %> + (<%= question.open_ended_blank_percentage.round(2) %>%) +
diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index f40d47d33..d6c9fc763 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -87,6 +87,26 @@ class Poll::Question < ApplicationRecord answer end + def open_ended_valid_answers_count + answers.count + end + + def open_ended_blank_answers_count + poll.voters.count - open_ended_valid_answers_count + end + + def open_ended_valid_percentage + return 0.0 if open_ended_total_answers.zero? + + (open_ended_valid_answers_count * 100.0) / open_ended_total_answers + end + + def open_ended_blank_percentage + return 0.0 if open_ended_total_answers.zero? + + (open_ended_blank_answers_count * 100.0) / open_ended_total_answers + end + private def find_by_attributes(user, option_id) @@ -96,4 +116,8 @@ class Poll::Question < ApplicationRecord { author: user } end end + + def open_ended_total_answers + open_ended_valid_answers_count + open_ended_blank_answers_count + end end diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index f4f37c0a9..ae79e1ba7 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -639,6 +639,9 @@ en: results: title: "Questions" most_voted_answer: "Most voted answer: " + open_ended: + valid: Valid + blank: Blank poll_header: back_to_proposal: Back to proposal poll_questions: diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index ca07be430..dd7e63c3e 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -639,6 +639,9 @@ es: results: title: "Preguntas" most_voted_answer: "Respuesta más votada: " + open_ended: + valid: Válidos + blank: En blanco poll_header: back_to_proposal: Volver a la propuesta poll_questions: diff --git a/spec/components/polls/results/question_component_spec.rb b/spec/components/polls/results/question_component_spec.rb new file mode 100644 index 000000000..562bb0d28 --- /dev/null +++ b/spec/components/polls/results/question_component_spec.rb @@ -0,0 +1,47 @@ +require "rails_helper" + +describe Polls::Results::QuestionComponent do + context "question that accepts options" do + let(:question) { create(:poll_question, :yes_no) } + let(:option_yes) { question.question_options.find_by(title: "Yes") } + let(:option_no) { question.question_options.find_by(title: "No") } + + it "renders results table content" do + create(:poll_answer, question: question, option: option_yes) + create(:poll_answer, question: question, option: option_no) + + render_inline Polls::Results::QuestionComponent.new(question) + + expect(page).to have_table with_rows: [{ "Most voted answer: Yes" => "1 (50.0%)", + "No" => "1 (50.0%)" }] + + page.find("table") do |table| + expect(table).to have_css "th.win", count: 1 + expect(table).to have_css "td.win", count: 1 + end + end + end + + context "question that does not accept options" do + let(:open_ended_question) { create(:poll_question_open) } + + it "renders open_ended headers and empty counts when there are no participants" do + render_inline Polls::Results::QuestionComponent.new(open_ended_question) + + expect(page).to have_table with_rows: [{ "Valid" => "0 (0.0%)", + "Blank" => "0 (0.0%)" }] + end + + it "renders counts and percentages provided by the model metrics" do + allow(open_ended_question).to receive_messages( + open_ended_valid_answers_count: 3, + open_ended_blank_answers_count: 1 + ) + + render_inline Polls::Results::QuestionComponent.new(open_ended_question) + + expect(page).to have_table with_rows: [{ "Valid" => "3 (75.0%)", + "Blank" => "1 (25.0%)" }] + end + end +end diff --git a/spec/components/polls/results_component_spec.rb b/spec/components/polls/results_component_spec.rb index 26c086215..d3a7257e3 100644 --- a/spec/components/polls/results_component_spec.rb +++ b/spec/components/polls/results_component_spec.rb @@ -3,47 +3,34 @@ require "rails_helper" describe Polls::ResultsComponent do let(:poll) { create(:poll) } - let(:question_1) { create(:poll_question, poll: poll, title: "Do you like Consul Democracy?") } - let(:option_yes) { create(:poll_question_option, question: question_1, title: "Yes") } - let(:option_no) { create(:poll_question_option, question: question_1, title: "No") } + let(:question_1) { create(:poll_question, :yes_no, poll: poll, title: "Do you like Consul Democracy?") } + let(:option_yes) { question_1.question_options.find_by(title: "Yes") } + let(:option_no) { question_1.question_options.find_by(title: "No") } - let(:question_2) { create(:poll_question, poll: poll, title: "What's your favorite color?") } - let(:option_blue) { create(:poll_question_option, question: question_2, title: "Blue") } - let(:option_green) { create(:poll_question_option, question: question_2, title: "Green") } - let(:option_yellow) { create(:poll_question_option, question: question_2, title: "Yellow") } + let(:question_2) { create(:poll_question, :abc, poll: poll, title: "Which option do you prefer?") } + let(:option_a) { question_2.question_options.find_by(title: "Answer A") } + let(:option_b) { question_2.question_options.find_by(title: "Answer B") } + let(:option_c) { question_2.question_options.find_by(title: "Answer C") } it "renders results content" do create_list(:poll_answer, 2, question: question_1, option: option_yes) create(:poll_answer, question: question_1, option: option_no) - create(:poll_answer, question: question_2, option: option_blue) - create(:poll_answer, question: question_2, option: option_green) - create(:poll_answer, question: question_2, option: option_yellow) + create(:poll_answer, question: question_2, option: option_a) + create(:poll_answer, question: question_2, option: option_b) + create(:poll_answer, question: question_2, option: option_c) render_inline Polls::ResultsComponent.new(poll) expect(page).to have_content "Do you like Consul Democracy?" + expect(page).to have_table "question_#{question_1.id}_results_table", + with_rows: [{ "Most voted answer: Yes" => "2 (66.67%)", + "No" => "1 (33.33%)" }] - page.find("#question_#{question_1.id}_results_table") do |table| - expect(table).to have_css "#option_#{option_yes.id}_result", text: "2 (66.67%)", normalize_ws: true - expect(table).to have_css "#option_#{option_no.id}_result", text: "1 (33.33%)", normalize_ws: true - end - - expect(page).to have_content "What's your favorite color?" - - page.find("#question_#{question_2.id}_results_table") do |table| - expect(table).to have_css "#option_#{option_blue.id}_result", text: "1 (33.33%)", normalize_ws: true - expect(table).to have_css "#option_#{option_green.id}_result", text: "1 (33.33%)", normalize_ws: true - expect(table).to have_css "#option_#{option_yellow.id}_result", text: "1 (33.33%)", normalize_ws: true - end - end - - it "renders results for polls with questions but without answers" do - poll = create(:poll, :expired, results_enabled: true) - question = create(:poll_question, poll: poll) - - render_inline Polls::ResultsComponent.new(poll) - - expect(page).to have_content question.title + expect(page).to have_content "Which option do you prefer?" + expect(page).to have_table "question_#{question_2.id}_results_table", + with_rows: [{ "Most voted answer: Answer A" => "1 (33.33%)", + "Answer B" => "1 (33.33%)", + "Answer C" => "1 (33.33%)" }] end end diff --git a/spec/models/poll/question_spec.rb b/spec/models/poll/question_spec.rb index f0f5e0882..f882b0503 100644 --- a/spec/models/poll/question_spec.rb +++ b/spec/models/poll/question_spec.rb @@ -188,4 +188,87 @@ RSpec.describe Poll::Question do end end end + + context "open-ended results" do + let(:poll) { create(:poll) } + let!(:question_open) { create(:poll_question_open, poll: poll) } + + it "includes voters who didn't answer any questions in blank answers count" do + create(:poll_voter, poll: poll) + + expect(question_open.open_ended_blank_answers_count).to eq 1 + expect(question_open.open_ended_valid_answers_count).to eq 0 + end + + describe "#open_ended_valid_answers_count" do + it "returns 0 when there are no answers" do + expect(question_open.open_ended_valid_answers_count).to eq 0 + end + + it "counts answers" do + create(:poll_answer, question: question_open, answer: "Hello") + create(:poll_answer, question: question_open, answer: "Bye") + + expect(question_open.open_ended_valid_answers_count).to eq 2 + end + end + + describe "#open_ended_blank_answers_count" do + let(:another_question) { create(:poll_question, :yes_no, poll: poll) } + let(:option_yes) { another_question.question_options.find_by(title: "Yes") } + let(:option_no) { another_question.question_options.find_by(title: "No") } + + it "counts valid participants of the poll who did not answer the open-ended question" do + voters = create_list(:poll_voter, 3, poll: poll) + voters.each do |voter| + create(:poll_answer, question: another_question, author: voter.user, option: option_yes) + end + create(:poll_answer, question: question_open, author: voters.sample.user, answer: "Free text") + + expect(question_open.open_ended_valid_answers_count).to eq 1 + expect(question_open.open_ended_blank_answers_count).to eq 2 + end + + it "returns 0 when there are no valid participants in the poll" do + expect(question_open.open_ended_blank_answers_count).to eq 0 + end + + it "counts every user one time even if they answered many questions" do + multiple_question = create(:poll_question_multiple, :abc, poll: poll) + option_a = multiple_question.question_options.find_by(title: "Answer A") + option_b = multiple_question.question_options.find_by(title: "Answer B") + another_question_open = create(:poll_question_open, poll: poll) + + voter = create(:poll_voter, poll: poll) + + create(:poll_answer, question: multiple_question, author: voter.user, option: option_a) + create(:poll_answer, question: multiple_question, author: voter.user, option: option_b) + create(:poll_answer, question: another_question, author: voter.user, option: option_yes) + create(:poll_answer, question: another_question_open, author: voter.user, answer: "Free text") + + expect(question_open.open_ended_blank_answers_count).to eq 1 + end + end + + describe "percentages" do + it "returns 0.0 when there aren't any answers" do + expect(question_open.open_ended_valid_percentage).to eq 0.0 + expect(question_open.open_ended_blank_percentage).to eq 0.0 + end + + it "calculates valid and blank percentages based on counts" do + another_question = create(:poll_question, :yes_no, poll: poll) + option_yes = another_question.question_options.find_by(title: "Yes") + + voters = create_list(:poll_voter, 4, poll: poll) + voters.each do |voter| + create(:poll_answer, question: another_question, author: voter.user, option: option_yes) + end + create(:poll_answer, question: question_open, author: voters.sample.user, answer: "A") + + expect(question_open.open_ended_valid_percentage).to eq 25.0 + expect(question_open.open_ended_blank_percentage).to eq 75.0 + end + end + end end