Extract admin poll results to component

Note that we have the same code in the officing section.
Then we can use the same component.

Note also that we are removing the parts of the system specs that are now
covered by the component itself, and taking the chance to unify tests.
In these removals and unifications we take into account that there are
other specs which already cover user interaction in this section.
This commit is contained in:
taitus
2025-09-16 10:01:19 +02:00
parent bc6506da5a
commit 7f376c3005
8 changed files with 118 additions and 195 deletions

View File

@@ -0,0 +1,20 @@
<div class="admin-poll-results-question">
<h3><%= question.title %></h3>
<table class="margin">
<thead>
<tr>
<th><%= t("admin.results.result.table_answer") %></th>
<th class="text-center"><%= t("admin.results.result.table_votes") %></th>
</tr>
</thead>
<tbody>
<% question.question_options.each_with_index do |option, i| %>
<% by_answer = by_question[question.id].present? ? by_question[question.id].group_by(&:answer) : {} %>
<tr id="question_<%= question.id %>_<%= i %>_result">
<td><%= option.title %></td>
<td class="text-center"><%= by_answer[option.title].present? ? by_answer[option.title].sum(&:amount) : 0 %></td>
</tr>
<% end %>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,12 @@
class Admin::Poll::Results::QuestionComponent < ApplicationComponent
attr_reader :question, :partial_results
def initialize(question, partial_results)
@question = question
@partial_results = partial_results
end
def by_question
@by_question ||= partial_results.group_by(&:question_id)
end
end

View File

@@ -1,21 +1,3 @@
<% by_question = @partial_results.group_by(&:question_id) %>
<% @poll.questions.each do |question| %>
<h3><%= question.title %></h3>
<table class="margin">
<thead>
<tr>
<th><%= t("admin.results.result.table_answer") %></th>
<th class="text-center"><%= t("admin.results.result.table_votes") %></th>
</tr>
</thead>
<tbody>
<% question.question_options.each_with_index do |option, i| %>
<% by_answer = by_question[question.id].present? ? by_question[question.id].group_by(&:answer) : {} %>
<tr id="question_<%= question.id %>_<%= i %>_result">
<td><%= option.title %></td>
<td class="text-center"><%= by_answer[option.title].present? ? by_answer[option.title].sum(&:amount) : 0 %></td>
</tr>
<% end %>
</tbody>
</table>
<%= render Admin::Poll::Results::QuestionComponent.new(question, @partial_results) %>
<% end %>

View File

@@ -27,27 +27,8 @@
</tbody>
</table>
<% by_question = @partial_results.group_by(&:question_id) %>
<% @poll.questions.each do |question| %>
<h3><%= question.title %></h3>
<table class="margin">
<thead>
<tr>
<th><%= t("admin.results.result.table_answer") %></th>
<th class="text-center"><%= t("admin.results.result.table_votes") %></th>
</tr>
</thead>
<tbody>
<% question.question_options.each_with_index do |option, i| %>
<% by_answer = by_question[question.id].present? ? by_question[question.id].group_by(&:answer) : {} %>
<tr id="question_<%= question.id %>_<%= i %>_result">
<td><%= option.title %></td>
<td class="text-center"><%= by_answer[option.title].present? ? by_answer[option.title].sum(&:amount) : 0 %></td>
</tr>
<% end %>
</tbody>
</table>
<%= render Admin::Poll::Results::QuestionComponent.new(question, @partial_results) %>
<% end %>
</div>
</div>

View File

@@ -0,0 +1,71 @@
require "rails_helper"
describe Admin::Poll::Results::QuestionComponent do
let(:poll) { create(:poll) }
let(:question) { create(:poll_question, poll: poll, title: "What do you want?") }
before do
create(:poll_question_option, question: question, title: "Yes")
create(:poll_question_option, question: question, title: "No")
end
it "renders question title and headers" do
render_inline Admin::Poll::Results::QuestionComponent.new(question, poll.partial_results)
expect(page).to have_css "h3", text: "What do you want?"
expect(page).to have_css "th", text: "Answer"
expect(page).to have_css "th", text: "Votes"
end
it "renders one row per option" do
render_inline Admin::Poll::Results::QuestionComponent.new(question, poll.partial_results)
expect(page).to have_css "tbody tr", count: 2
expect(page).to have_css "td", text: "Yes"
expect(page).to have_css "td", text: "No"
end
it "sums votes by answer title" do
create(:poll_partial_result, question: question, answer: "Yes", amount: 2)
create(:poll_partial_result, question: question, answer: "Yes", amount: 1)
create(:poll_partial_result, question: question, answer: "No", amount: 5)
render_inline Admin::Poll::Results::QuestionComponent.new(question, question.partial_results)
page.find("tr#question_#{question.id}_0_result") do |yes_result|
expect(yes_result).to have_css "td", text: "Yes"
expect(yes_result).to have_css "td", text: "3"
end
page.find("tr#question_#{question.id}_1_result") do |no_result|
expect(no_result).to have_css "td", text: "No"
expect(no_result).to have_css "td", text: "5"
end
end
it "shows 0 when an option has no partial results" do
render_inline Admin::Poll::Results::QuestionComponent.new(question, poll.partial_results)
page.find("tr#question_#{question.id}_0_result") do |yes_result|
expect(yes_result).to have_css "td", text: "Yes"
expect(yes_result).to have_css "td", text: "0"
end
page.find("tr#question_#{question.id}_1_result") do |no_result|
expect(no_result).to have_css "td", text: "No"
expect(no_result).to have_css "td", text: "0"
end
end
it "ignores partial results from other questions" do
other_question = create(:poll_question, poll: poll)
create(:poll_question_option, question: other_question, title: "Yes")
create(:poll_question_option, question: other_question, title: "Irrelevant")
create(:poll_partial_result, question: other_question, answer: "Yes", amount: 9)
create(:poll_partial_result, question: other_question, answer: "Irrelevant", amount: 9)
render_inline Admin::Poll::Results::QuestionComponent.new(question, poll.partial_results)
expect(page).to have_css "td", text: "0", count: 2
end
end

View File

@@ -228,7 +228,7 @@ describe "Admin booths assignments", :admin do
expect(page).not_to have_css "#recounts_list"
end
scenario "Results for a booth assignment" do
scenario "Recounts for a booth assignment" do
poll = create(:poll)
booth_assignment = create(:poll_booth_assignment, poll: poll)
other_booth_assignment = create(:poll_booth_assignment, poll: poll)
@@ -285,30 +285,6 @@ describe "Admin booths assignments", :admin do
click_link "Results"
expect(page).to have_content(question_1.title)
within("#question_#{question_1.id}_0_result") do
expect(page).to have_content("Yes")
expect(page).to have_content(11)
end
within("#question_#{question_1.id}_1_result") do
expect(page).to have_content("No")
expect(page).to have_content(4)
end
expect(page).to have_content(question_2.title)
within("#question_#{question_2.id}_0_result") do
expect(page).to have_content("Today")
expect(page).to have_content(5)
end
within("#question_#{question_2.id}_1_result") do
expect(page).to have_content("Tomorrow")
expect(page).to have_content(6)
end
within("#white_results") { expect(page).to have_content("21") }
within("#null_results") { expect(page).to have_content("44") }
within("#total_results") { expect(page).to have_content("66") }

View File

@@ -343,46 +343,6 @@ describe "Admin polls", :admin do
expect(page).to have_content "There are no results"
end
scenario "Show partial results" do
poll = create(:poll)
booth_assignment_1 = create(:poll_booth_assignment, poll: poll)
booth_assignment_2 = create(:poll_booth_assignment, poll: poll)
booth_assignment_3 = create(:poll_booth_assignment, poll: poll)
question_1 = create(:poll_question, poll: poll)
create(:poll_question_option, title: "Oui", question: question_1)
create(:poll_question_option, title: "Non", question: question_1)
question_2 = create(:poll_question, poll: poll)
create(:poll_question_option, title: "Aujourd'hui", question: question_2)
create(:poll_question_option, title: "Demain", question: question_2)
[booth_assignment_1, booth_assignment_2, booth_assignment_3].each do |ba|
create(:poll_partial_result,
booth_assignment: ba,
question: question_1,
answer: "Oui",
amount: 11)
create(:poll_partial_result,
booth_assignment: ba,
question: question_2,
answer: "Demain",
amount: 5)
end
create(:poll_recount,
booth_assignment: booth_assignment_1,
white_amount: 21,
null_amount: 44,
total_amount: 66)
visit admin_poll_results_path(poll)
expect(page).to have_content "Results by booth"
end
scenario "Enable stats and results for booth polls" do
unvoted_poll = create(:poll)
@@ -418,8 +378,9 @@ describe "Admin polls", :admin do
expect(page).not_to have_content "Results by booth"
end
scenario "Results by answer" do
scenario "Show results, recount and details by booth" do
poll = create(:poll)
booth_assignment_1 = create(:poll_booth_assignment, poll: poll)
booth_assignment_2 = create(:poll_booth_assignment, poll: poll)
booth_assignment_3 = create(:poll_booth_assignment, poll: poll)
@@ -427,7 +388,7 @@ describe "Admin polls", :admin do
question_1 = create(:poll_question, :yes_no, poll: poll)
question_2 = create(:poll_question, poll: poll)
create(:poll_question_option, title: "Today", question: question_2)
create(:poll_question_option, title: "Today", question: question_2)
create(:poll_question_option, title: "Tomorrow", question: question_2)
[booth_assignment_1, booth_assignment_2, booth_assignment_3].each do |ba|
@@ -436,12 +397,14 @@ describe "Admin polls", :admin do
question: question_1,
answer: "Yes",
amount: 11)
create(:poll_partial_result,
booth_assignment: ba,
question: question_2,
answer: "Tomorrow",
amount: 5)
end
create(:poll_recount,
booth_assignment: booth_assignment_1,
white_amount: 21,
@@ -449,60 +412,21 @@ describe "Admin polls", :admin do
total_amount: 66)
visit admin_poll_path(poll)
click_link "Results"
expect(page).to have_content(question_1.title)
question_1.question_options.each_with_index do |option, i|
within("#question_#{question_1.id}_#{i}_result") do
expect(page).to have_content(option.title)
expect(page).to have_content([33, 0][i])
end
end
expect(page).to have_content(question_2.title)
question_2.question_options.each_with_index do |option, i|
within("#question_#{question_2.id}_#{i}_result") do
expect(page).to have_content(option.title)
expect(page).to have_content([0, 15][i])
end
end
expect(page).to have_content "Results by booth"
within("#white_results") { expect(page).to have_content("21") }
within("#null_results") { expect(page).to have_content("44") }
within("#total_results") { expect(page).to have_content("66") }
end
scenario "Link to results by booth" do
poll = create(:poll)
booth_assignment1 = create(:poll_booth_assignment, poll: poll)
booth_assignment2 = create(:poll_booth_assignment, poll: poll)
expect(page).to have_link("See results", count: 3)
question = create(:poll_question, :yes_no, poll: poll)
create(:poll_partial_result,
booth_assignment: booth_assignment1,
question: question,
answer: "Yes",
amount: 5)
create(:poll_partial_result,
booth_assignment: booth_assignment2,
question: question,
answer: "Yes",
amount: 6)
visit admin_poll_path(poll)
click_link "Results"
expect(page).to have_link("See results", count: 2)
within("#booth_assignment_#{booth_assignment1.id}_result") do
within("#booth_assignment_#{booth_assignment_1.id}_result") do
click_link "See results"
end
expect(page).to have_content booth_assignment1.booth.name
expect(page).to have_content booth_assignment_1.booth.name
expect(page).to have_content "Results"
expect(page).to have_content "Yes"
expect(page).to have_content "5"

View File

@@ -123,6 +123,9 @@ describe "Officing Results", :with_frozen_time do
click_link "See results"
end
expect(page).to have_content(I18n.l(Date.current.to_date, format: :long))
expect(page).to have_content(booth_name)
within("#question_#{question_1.id}_0_result") do
expect(page).to have_content("5555")
expect(page).not_to have_content("7777")
@@ -134,50 +137,4 @@ describe "Officing Results", :with_frozen_time do
within("#question_#{question_1.id}_0_result") { expect(page).to have_content("5555") }
within("#question_#{question_1.id}_1_result") { expect(page).to have_content("200") }
end
scenario "Index lists all questions and answers" do
officer_assignment = poll_officer.officer_assignments.first
booth_assignment = officer_assignment.booth_assignment
booth = booth_assignment.booth
create(
:poll_partial_result,
officer_assignment: officer_assignment,
booth_assignment: booth_assignment,
date: poll.ends_at,
question: question_1,
amount: 33
)
create(
:poll_recount,
officer_assignment: officer_assignment,
booth_assignment: booth_assignment,
date: poll.ends_at,
white_amount: 21,
null_amount: 44,
total_amount: 66
)
visit officing_poll_results_path(poll,
date: I18n.l(poll.ends_at.to_date),
booth_assignment_id: officer_assignment.booth_assignment_id)
expect(page).to have_content(I18n.l(poll.ends_at.to_date, format: :long))
expect(page).to have_content(booth.name)
expect(page).to have_content(question_1.title)
question_1.question_options.each_with_index do |answer, i|
within("#question_#{question_1.id}_#{i}_result") { expect(page).to have_content(answer.title) }
end
expect(page).to have_content(question_2.title)
question_2.question_options.each_with_index do |answer, i|
within("#question_#{question_2.id}_#{i}_result") { expect(page).to have_content(answer.title) }
end
within("#white_results") { expect(page).to have_content("21") }
within("#null_results") { expect(page).to have_content("44") }
within("#total_results") { expect(page).to have_content("66") }
end
end